kotlin 中泛型中 <out Any?> 和 <*> 的区别

问题描述 投票:0回答:3

我不明白泛型中

<out Any?>
<*>
之间的区别。我知道使用
<*>
就像同时执行
<out Any?>
<in Nothing>
,但使用
<out Any?>
会导致相同的结果。

kotlin generics variance any nothing
3个回答
4
投票

无界

相同
对于通用类型
*
投影与
Invariant<T>
Invariant<out Any?>
Invariant<in Nothing>
Covariant<out T>
Covariant<out Any?>
Contravariant<in T>
Contravariant<in Nothing>

有界限

相同
对于通用类型
*
投影与
Invariant<T : SomeType>
Invariant<SomeType>
Invariant<in Nothing>
Covariant<out T : SomeType>
Covariant<out SomeType>
Contravariant<in SomeType : T>
不支持下限

3
投票

主要区别在于,您不能在声明为逆变的类型参数上使用

out Any?
投影(在声明位置使用
in
)——它的所有使用位置都必须显式或隐式
in
-也预计了。

此外,对于具有上限

T : TUpper
的类型参数,您不能将
out
投影与不是
TUpper
子类型的类型参数一起使用。例如,如果类型声明为
Foo<T : Number>
,则投影
Foo<out Any?>
无效。在
out
的情况下,星形投影的
Foo<*>
部分表示上限,而不是
Any?


0
投票

记住星投影行为的最简单方法是记住这张表:

将 `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"))*/
}
© www.soinside.com 2019 - 2024. All rights reserved.