Kotlin 中 KProperty1 的通用扩展
Generic extensions of KProperty1 in Kotlin
我有以下代码:
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
class 创建扩展函数,但我的泛型与我预期的不匹配。 main()
列出了这两个函数的一些用法,最后有 3 个意想不到的结果,并描述了我应该期待的结果。
首先,请注意 KProperty1<T, out R>
has an out
-projected type parameter R
,因此 KProperty1<Foo, Bar>
的实例也可以用于需要 KProperty1<Foo, Baz>
的地方,其中 Baz
是 [= 的超类型18=],例如Any
.
这正是您使用不完全匹配的类型调用 eq
时发生的情况:使用更常见的超类型以便正确解析调用。
例如,这个调用:
Person::age eq 1.2f
实际上等同于:
Person::age.eq<Person, Any>(1.2f)
您甚至可以将其自动转换为这种形式,应用将中缀调用替换为普通调用,然后添加显式类型参数.
因此,只要类型不完全匹配,就会为 R
选择一个通用的超类型。目前,没有正确的方法告诉编译器它不应该回退到超类型(参见 )。
最后一次调用是类型推断当前工作方式的副作用:似乎编译器在决定它是否兼容可空性之前必须选择其中一个重载。如果您将 null
替换为 (null as String?)
,它会起作用。请在 kotl.in/issue.
提交问题
一般来说,最好不要将通用签名与非通用签名混合使用,因为通用替换可能会导致歧义。在您的情况下,替换 R := KProperty1<T, Foo>
(即在这种类型的 属性 上使用 eq
)会导致歧义。
也相关:
(基本上是相同的情况,但是这里对类型推断的工作原理有更详细的解释)
我有以下代码:
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
class 创建扩展函数,但我的泛型与我预期的不匹配。 main()
列出了这两个函数的一些用法,最后有 3 个意想不到的结果,并描述了我应该期待的结果。
首先,请注意 KProperty1<T, out R>
has an out
-projected type parameter R
,因此 KProperty1<Foo, Bar>
的实例也可以用于需要 KProperty1<Foo, Baz>
的地方,其中 Baz
是 [= 的超类型18=],例如Any
.
这正是您使用不完全匹配的类型调用 eq
时发生的情况:使用更常见的超类型以便正确解析调用。
例如,这个调用:
Person::age eq 1.2f
实际上等同于:
Person::age.eq<Person, Any>(1.2f)
您甚至可以将其自动转换为这种形式,应用将中缀调用替换为普通调用,然后添加显式类型参数.
因此,只要类型不完全匹配,就会为 R
选择一个通用的超类型。目前,没有正确的方法告诉编译器它不应该回退到超类型(参见
最后一次调用是类型推断当前工作方式的副作用:似乎编译器在决定它是否兼容可空性之前必须选择其中一个重载。如果您将 null
替换为 (null as String?)
,它会起作用。请在 kotl.in/issue.
一般来说,最好不要将通用签名与非通用签名混合使用,因为通用替换可能会导致歧义。在您的情况下,替换 R := KProperty1<T, Foo>
(即在这种类型的 属性 上使用 eq
)会导致歧义。
也相关:
(基本上是相同的情况,但是这里对类型推断的工作原理有更详细的解释)