Kotlin-通用类型参数不受尊重 [英] Kotlin - Generic Type Parameters Not Being Respected
问题描述
请考虑以下示例:
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"
}
鉴于test
期望类型为R
的value
,为什么在这种情况下,如果属性类型为Int
,它是否允许我传递String
?
Given that test
expects a value
of type R
, why in this context, where the property type is Int
, does it allow me to pass a String
?
推荐答案
首先,查看接口KProperty1
的声明,该声明为:
First, take a look at the declaration of the interface KProperty1
, which is:
interface KProperty1<T, out R> : KProperty<R>, (T) -> R
这里的重要部分是 out
-投影的类型参数R
,它定义了KProperty1
类型之间的子类型关系,并为R
使用了不同的类型参数.
The important part here is the out
-projected type parameter R
, which defines subptyping relationships between the KProperty1
types with different type arguments used for R
.
(1) 即,对于任何Foo
,A
和B
,使得A : B
(A
是
KProperty1<Foo, A> : KProperty1<Foo, B>
.之所以称为协方差,是因为参数化类型彼此之间的关联方式与其类型参数相同.
(1) Namely, for any Foo
, A
and B
such that A : B
(A
is a subtype of B
), KProperty1<Foo, A> : KProperty1<Foo, B>
. This is called covariance, because the parameterized types relate to each other in the same way as their type arguments do.
(2) 接下来,请注意,对于任何A
和B
,使得A : B
可以将A
的实例作为任何B
类型参数的参数.扩展功能的接收器参数在这方面与普通参数没有什么不同.
(2) Next, note that for any A
and B
such that A : B
, an instance of A
can be passed as an argument to any B
-typed parameter. Receiver parameters of extension functions are not different from normal parameters in this respect.
现在,关键部分是编译器运行的类型推断算法.类型推断的目标之一是为每个泛型调用建立静态已知的类型实参,而忽略类型实参.
Now, the crucual part is the type inference algorithm that the compiler runs. One of the goals of type inference is to establish statically-known type arguments for each generic call where type arguments are omitted.
在调用Foo::bar test "Hello"
的类型推断期间,编译器需要根据接收方Foo::bar
(KProperty1<Foo, Int>
)和R
的类型参数. >参数"Hello"
(String
).
During type inference for the call Foo::bar test "Hello"
, the compiler needs to actually infer the type arguments for T
and R
based on the known types of the receiver Foo::bar
(KProperty1<Foo, Int>
) and the value
argument "Hello"
(String
).
这是通过解决约束系统在内部完成的.我们可以如下模拟此逻辑:
This is done internally by solving a constraint system. We could emulate this logic as follows:
-
鉴于
KProperty<Foo, Int>
作为KProperty<T, R>
传递:
- 我们必须使用
T := Foo
(因为T
是不变的) - 我们必须使用
Int
或其任何超类型作为类型参数R
- 这是因为
R
的协方差:给定 (1) 和 (2) ,必须为R
选择Int
或它的某些超类型才能在期望KProperty<Foo, R>
的地方传递KProperty<Foo, Int>
- 这些超类型的示例是
Int?
,Number
,Number?
,Any
,Any?
- we must use
T := Foo
(asT
is invariant) - we must use
Int
or any of its supertypes as the type argumentR
- this is because of covariance for
R
: given (1) and (2) combined, choosingInt
or some of its supertypes forR
is necessary to be able to passKProperty<Foo, Int>
whereKProperty<Foo, R>
is expected - examples of these supertypes are
Int?
,Number
,Number?
,Any
,Any?
鉴于
String
作为R
传递:- 我们必须使用
String
或其某些超类型作为R
- 这是能够通过
String
的必要条件,其中由于 (2) - 这些超类型的示例是
String?
,CharSequence
,CharSequence?
,Any
,Any?
- we must use
String
or some of its supertypes asR
- this is necessary to be able to pass a
String
whereR
is expected due to (2) - examples of these supertypes are
String?
,CharSequence
,CharSequence?
,Any
,Any?
鉴于
R
的两个约束,即它应该是Int
或其某些超类型,并且它应该是String
或其某些超型,编译器会找到同时满足这两个条件的最不常见的类型.此类型为Any
.Given the two constraints on
R
, namely that it should beInt
or some of its supertypes and it should beString
or some of its supertypes, the compiler finds the least common type that satisfies both. This type isAny
.因此,推断出的类型参数是
T := Foo
和R := Any
,使用显式类型参数的调用将是:So, the inferred type arguments are
T := Foo
andR := Any
, and the call with explicit type arguments would be:Foo::bar.test<Foo, Any>("Hello")
在IntelliJ IDEA中,您可以在非插入调用上使用操作添加显式类型参数来添加推断的类型.
In IntelliJ IDEA, you can use an action Add explicit type arguments on a non-infix call to add the inferred types.
免责声明:这并不完全是编译器内部工作的方式,但是使用这种推理方式,您可能经常会得到与编译器结果一致的结果.
也相关:
- Kotlin generics: counterintuitive type inference and checking with out keyword
- Generic extensions of KProperty1 in Kotlin
这篇关于Kotlin-通用类型参数不受尊重的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
- this is necessary to be able to pass a
- 这是能够通过
- this is because of covariance for
- 这是因为