Kotlin中KProperty1的通用扩展

时间:2018-10-09 09:45:19

标签: reflection kotlin kotlin-reflect

我有以下代码:

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个意外结果,并说明了我应该期望的结果。

1 个答案:

答案 0 :(得分:2)

首先,请注意,KProperty1<T, out R>有一个out-projected type parameter R,因此在期望$sql = "INSERT INTO table_name (column1, column2...) VALUES "; foreach($res $key => $val) { $sql .= "($val['column1'],$val['column2']...),"; } $sql = rtrim($sql, ','); ... KProperty1<Foo, Bar>的地方也可以使用KProperty1<Foo, Baz>的实例Baz的超类型,例如Bar

当您使用不完全匹配的类型调用Any时,就会发生这种情况:使用了更常见的超类型,以便调用能够正确解析。

例如,此呼叫:

eq

实际上等效于:

Person::age eq 1.2f

您甚至可以通过应用用普通调用替换infix调用,然后添加显式类型参数 ,来自动将其转换为这种形式。

因此,只要类型不完全匹配,就会为Person::age.eq<Person, Any>(1.2f) 选择一个公共的超类型。当前,没有正确的方法告诉编译器它不应该退回到父类型(请参见this Q&A)。

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

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