reflection - Kotlin 中 KProperty1 的通用扩展
问题描述
我有以下代码:
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 个意想不到的结果,并描述了我应该期待什么。
解决方案
首先,请注意KProperty1<T, out R>
具有out
-projected 类型参数R
,因此KProperty1<Foo, Bar>
也可以在KProperty1<Foo, Baz>
预期 a 的地方使用 的实例,其中Baz
是 的超类型Bar
,例如Any
。
这正是eq
使用不完全匹配的类型调用时发生的情况:使用了更常见的超类型,以便正确解析调用。
例如,这个调用:
Person::age eq 1.2f
等效于:
Person::age.eq<Person, Any>(1.2f)
您甚至可以自动将其转换为这种形式,即用普通调用替换中缀调用,然后添加显式类型参数。
因此,只要类型不完全匹配,就会为R
. 目前,没有适当的方法告诉编译器它不应该退回到超类型(请参阅此 Q&A)。
最后一次调用是类型推断当前工作方式的副作用:编译器似乎必须在决定它是否兼容可空性之前选择其中一个重载。如果你null
用它代替(null as String?)
它。请在kotl.in/issue上为此提出问题。
通常,最好不要将通用签名与非通用签名混合,因为可能的通用替换会导致歧义。在您的情况下,替换R := KProperty1<T, Foo>
(即eq
在这种类型的属性上使用)会导致歧义。
也相关:
-
(基本上相同的情况,但在这里更详细地解释了类型推断的工作原理)
推荐阅读
- c# - 如何避免循环引用 asp.net core mvc [HttpPost("add-recipe")] web api
- vue.js - 使用scss文件时Vue“找不到这些相关模块”
- html - 在 flexdashboard 中添加指向 PNG 的超链接
- python - Keras 子类化类型错误:tf__call() 为参数“训练”获得了多个值
- ansible - 在 Ansible 中处理跳过的主机
- node.js - waitForSelector 突然不再在 puppeteer 中工作
- python - 如何使用python获取当前时间
- javascript - 无法让状态在本机反应中工作
- python - 如何在 chemspipy(化学蜘蛛)中返回实验或估计的化学性质?
- javascript - 当密码与确认密码字段 Javascript 不匹配时,为什么我的表单没有返回警报?