Kotlin中KProperty1的通用扩展 [英] Generic extensions of KProperty1 in Kotlin

查看:232
本文介绍了Kotlin中KProperty1的通用扩展的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下代码:

import kotlin.reflect.KProperty1


infix fun <T, R> KProperty1<T, R>.eq(value: R) {
    println(this.name + " = $value")
}

infix fun <T, R> KProperty1<T, R>.eq(value: KProperty1<T, R>) {
    println(this.name + " = " + value.name)
}

data class Person(val age: Int, val name: String, val surname: String?)

fun main() {
    Person::age eq 1  // Valid. First function
    Person::name eq "Johan"  // Valid. First function
    Person::name eq Person::name  // Valid. Second function
    Person::age eq 1.2f  // Valid, but it shouldn't be. First function
    Person::age eq Person::name  // Valid, but it shouldn't be. First function
    Person::surname eq null  // Invalid, but it should be. Second function, but it should be first
}

我正在尝试使用泛型为KProperty1类创建扩展功能,但是我的泛型与我期望的不匹配. main()列出了这两个函数的一些用法,最后有3个意外结果,并描述了我应该期待的事情.

解决方案

首先,请注意此问与答相同).

最后一个调用是类型推断当前工作方式的副作用:似乎编译器必须先选择一个重载,然后才能确定它是否兼容可空性.如果将null替换为(null as String?),它将起作用.为此,请在 kotl.in/issue 中提交问题.

通常,最好不要将通用签名与非通用签名混合使用,因为可能导致通用替换而导致歧义.在您的情况下,替换R := KProperty1<T, Foo>(即在此类型的属性上使用eq)将导致歧义.

也相关:

I have the following code:

import kotlin.reflect.KProperty1


infix fun <T, R> KProperty1<T, R>.eq(value: R) {
    println(this.name + " = $value")
}

infix fun <T, R> KProperty1<T, R>.eq(value: KProperty1<T, R>) {
    println(this.name + " = " + value.name)
}

data class Person(val age: Int, val name: String, val surname: String?)

fun main() {
    Person::age eq 1  // Valid. First function
    Person::name eq "Johan"  // Valid. First function
    Person::name eq Person::name  // Valid. Second function
    Person::age eq 1.2f  // Valid, but it shouldn't be. First function
    Person::age eq Person::name  // Valid, but it shouldn't be. First function
    Person::surname eq null  // Invalid, but it should be. Second function, but it should be first
}

I am trying create extension functions for the KProperty1 class with generics, but my generics are not matching as I'd expect. main() lists some uses of the two functions, with 3 unexpected results at the end and a description of what I should expect.

解决方案

First, note that KProperty1<T, out R> has an out-projected type parameter R, so an instance of KProperty1<Foo, Bar> can also be used where a KProperty1<Foo, Baz> is expected, where Baz is a supertype of Bar, such as Any.

This is exactly what happens when you call eq with types that don't quite match: a more common supertype is used so that the call resolves correctly.

For example, this call:

Person::age eq 1.2f

is effectively equivalent to:

Person::age.eq<Person, Any>(1.2f)

You can even transform it to this form automatically be applying Replace infix call with ordinary call and then Add explicit type arguments.

So whenever the types don't match exactly, a common supertype is chosen for R. Currently, there's no proper way to tell the compiler it should not fall back to the supertype (see this Q&A).

The last call is a side effect of how type inference currently works: it seems like the compiler has to choose one of the overloads before it decides on whether it is compatible for nullability. If you replace null with (null as String?) it works. Please file an issue for this at kotl.in/issue.

In general, it is preferable not to mix the generic signatures with non-generic ones because of possible generic substitutions that lead to ambiguity. In your case, a substitution R := KProperty1<T, Foo> (i.e. using eq on a property of this type) would lead to ambiguity.

Also relevant:

这篇关于Kotlin中KProperty1的通用扩展的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆