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 )会导致歧义。

也相关:

  • (基本上是相同的情况,但是这里对类型推断的工作原理有更详细的解释)