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
类创建扩展功能,但是我的泛型与我期望的不匹配. main()
列出了这两个函数的一些用法,最后有3个意外结果,并描述了我应该期待的事情.
首先,请注意 out
-投影类型参数R
,因此在需要KProperty1<Foo, Baz>
的情况下也可以使用KProperty1<Foo, Bar>
的实例,其中Baz
是超类型Bar
,例如Any
.
当您使用不完全匹配的类型调用eq
时,这正是发生的情况:使用了更常见的超类型,以便调用能够正确解析.
例如,此呼叫:
Person::age eq 1.2f
实际上等效于:
Person::age.eq<Person, Any>(1.2f)
您甚至可以通过应用用普通调用替换infix调用,然后添加显式类型参数 ,自动将其转换为这种形式. >
因此,只要类型不完全匹配,就会为R
选择一个通用的超类型.当前,没有正确的方法告诉编译器它不应该退回到父类型(请参阅 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 - Generic Type Parameters Not Being Respected
(basically the same case, but with a more detailed explanation of how type inference works here)
这篇关于Kotlin中KProperty1的通用扩展的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!