我有以下代码:
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个意外结果,并说明了我应该期望的结果。
答案 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>
)会导致歧义。