我不明白泛型中
<out Any?>
和<*>
之间的区别。我知道使用 <*>
就像同时执行 <out Any?>
和 <in Nothing>
,但使用 <out Any?>
会导致相同的结果。
对于通用类型 | 投影与 |
---|---|
|
和
|
|
|
|
|
对于通用类型 | 投影与 |
---|---|
|
和
|
|
|
|
不支持下限 |
主要区别在于,您不能在声明为逆变的类型参数上使用
out Any?
投影(在声明位置使用 in
)——它的所有使用位置都必须显式或隐式 in
-也预计了。
此外,对于具有上限
T : TUpper
的类型参数,您不能将 out
投影与不是 TUpper
子类型的类型参数一起使用。例如,如果类型声明为 Foo<T : Number>
,则投影 Foo<out Any?>
无效。在 out
的情况下,星形投影的 Foo<*>
部分表示上限,而不是 Any?
。
记住星投影行为的最简单方法是记住这张表:
将 `T` 声明为 | “T”上的星投影相当于 |
---|---|
由于隐式/默认上限为
Any?
(即 <T>
相当于 <T: Any?>
),因此无需将此表分成两个。
其他答案没有提到的一个根本区别是:
如果我们在代码片段中看到
<out Any>
,它可能是声明站点方差,或使用站点方差,具体取决于它的使用位置;
如果我们在代码片段中看到
<*>
,那么它始终是使用站点差异。 星形投影只是类型投影的一种特殊类型。
星投影的行为在文档中有很好的描述,但有点冗长:
Kotlin 为此提供了所谓的星型投影语法:
对于
,其中Foo<out T : TUpper>
是协变类型参数 上限T
,TUpper
等价于Foo<*>
。这 意味着当Foo<out TUpper>
未知时,您可以安全地读取T
的值 来自TUpper
。Foo<*>
对于
,其中Foo<in T>
是逆变类型参数,T
是 相当于Foo<*>
。这意味着你无能为力 当Foo<in Nothing>
未知时,以安全的方式写入Foo<*>
。T
对于
,其中Foo<T : TUpper>
是一个不变类型参数, 上限T
,TUpper
等价于Foo<*>
读取值并按Foo<out TUpper>
写入值。Foo<in Nothing>
这一行特定的描述是多余的:
相当于读取值的Foo<*>
和写入值的Foo<out TUpper>
。Foo<in Nothing>
可以简化为:
相当于Foo<*>
Foo<out TUpper>
这是因为
Foo<out TUpper>
在实践中的行为与写入值的 Foo<in Nothing>
完全相同——我们无法写入它。演示:
class Box<T: Number>(var item: T) {
fun get(): T = item
fun set(t: T) { this.item = t }
}
fun boxHandler1(box: Box<*>){
//reading is possible, resolved to the upperbound at declaration site
val number: Number = box.get()
//writing is not possible, parameter of `t` of `set` is `Nothing`
/*box.set(TODO("Impossible"))*/
}
fun boxHandler2(box: Box<out Number>){
//reading is possible, resolved to the upperbound at declaration site
val number: Number = box.get()
//writing is (obviously) not possible, because covariance projection is applied
/*box.set(TODO("Impossible"))*/
}
fun boxHandler3(box: Box<in Nothing>){
//reading is possible, but resolved to `Any?` due to the use of `in Nothing`
val any: Any? = box.get()
//writing is not possible, `T` is resolved to `Nothing` when using `in Nothing`
/*box.set(TODO("Impossible"))*/
}