ser*_*0ne 7 generics type-inference typechecking type-parameter kotlin
考虑以下示例:
import kotlin.reflect.KProperty1
infix fun <T, R> KProperty1<T, R>.test(value: R) = Unit
data class Foo(val bar: Int)
fun main() {
Foo::bar test "Hello"
}
Run Code Online (Sandbox Code Playgroud)
给定test期望a value的类型R,为什么在这种情况下,在属性类型为的地方Int,它是否允许我传递a String?
首先,看一下接口的声明KProperty1,它是:
interface KProperty1<T, out R> : KProperty<R>, (T) -> R
Run Code Online (Sandbox Code Playgroud)
这里最重要的部分是out-projected 型参数R,它定义subptyping的之间的关系KProperty1类型的具有用于不同类型的参数R。
(1)即,对于任何Foo,A并且B使得A : B(A是的子类型B)KProperty1<Foo, A> : KProperty1<Foo, B>。这称为协方差,因为参数化类型彼此之间的关联方式与其类型参数一样。
(2)接下来,请注意,对于anyA和BthatA : B,A可以将的实例作为参数传递给任何B-typed参数。在这方面,扩展功能的接收器参数与普通参数没有区别。
现在,最关键的部分是编译器运行的类型推断算法。类型推断的目标之一是为每个泛型调用建立静态已知的类型实参,其中忽略类型实参。
在类型推断呼叫Foo::bar test "Hello",编译器需要实际推断类型参数T,并R根据已知类型的接收器的Foo::bar(KProperty1<Foo, Int>)和value参数"Hello"(String)。
这是通过解决约束系统在内部完成的。我们可以如下模拟此逻辑:
鉴于KProperty<Foo, Int>传递为KProperty<T, R>:
T := Foo(T不变)Int或其任何超类型作为类型实参R
R:给定的(1)和(2)相结合,选择Int或它的一些超类型用于R有必要能够通过KProperty<Foo, Int>,其中KProperty<Foo, R>,预计Int?,Number,Number?,Any,Any?给定a String作为传递R:
String其某些超型作为R
String位置,这是必需的RString?,CharSequence,CharSequence?,Any,Any?给定的两个约束R,即它应该是Int或其某些超类型,并且应该是String或其某些超类型,编译器会找到同时满足这两个条件的最不常见类型。此类型为Any。
因此,推断的类型实参为T := Foo和R := Any,使用显式类型实参的调用为:
Foo::bar.test<Foo, Any>("Hello")
Run Code Online (Sandbox Code Playgroud)
在IntelliJ IDEA中,可以使用对非infix调用添加显式类型参数的操作来添加推断的类型。
免责声明:这并不是编译器内部的确切工作方式,但是使用这种推理方式,您可能经常会得到与编译器结果一致的结果。
也相关: