sun*_*r20 -1 java generics types kotlin type-projection
我正在阅读并试图理解 Kotlin 类型的预测,有时我会想出这样令人困惑的事情:
对于逆变类型参数,例如
Consumer<in T>,星形投影等效于<in Nothing>。实际上,您不能调用具有T此类星形投影签名的任何方法。 如果类型参数是逆变的,则它仅充当消费者,并且正如我们之前讨论的那样,您并不确切知道它可以消耗什么。因此,你不能给它任何东西来消费。当有关类型参数的信息不重要时,您可以使用星形投影语法:您不使用任何引用签名中类型参数的方法,或者您只读取数据而不关心其特定类型。例如,您可以将
printFirst函数List<*>作为参数来实现。
具有星形投影的逆变类型是什么意思,它是如何实现的
Yog*_*ity 11
让我们了解逆变的星形投影等于 的原因。但在此之前,我们需要了解星形投影到底是什么以及它如何用于协变。Consumer<in T><in Nothing>Producer<out T>
T当您使用某个泛型类的星形投影时,您对使用从该泛型类返回或接受作为参数的函数或属性不感兴趣T。例如,以下函数仅返回List两者中较大的一个。size我们只对不返回或接受的属性感兴趣T,它只返回一个Int:
例子
fun getBiggerOfTwo(list1: MutableList<*>, list2: MutableList<*>) : MutableList<*> {
return if (list1.size >= list2.size) list1 else list2
}
Run Code Online (Sandbox Code Playgroud)
为什么使用*而不是指定类型?
我们希望保持类型未知,这样我们就不会
T意外地使用特定的函数。例如,在上面的函数中,如果我们被允许调用list1.add(Something()),我们可能最终会在我们只想比较列表的地方意外地改变列表。因此,当我们调用该函数时,编译器将通过标记错误来帮助我们add()。除了创建安全性之外,我们的函数还将可重用于各种类型,而不仅仅是某些特定类型。
在以下示例中,我们将使用Producer和Consumer类来生成和使用 的各种子类型class Pet(val cutenessIndex: String):
申报现场
class Producer<out T> {
private val items = listOf<T>()
fun produce() : T = items.last()
fun size(): Int = items.size
}
Run Code Online (Sandbox Code Playgroud)
使用现场
fun useProducer(star: Producer<*>) {
// Produces Any?, a Pet is not guaranteed because T is unknown
val anyNullable = star.produce() // Not useful
// Can't use functions and properties of Pet.
anyNullable.cutenessIndex // Error
// Can use T-independent functions and properties
star.size() // OK
}
Run Code Online (Sandbox Code Playgroud)
申报现场
class Producer<out T : Pet> {
private val pets = listOf<T>()
fun produce() : T = pets.last()
}
Run Code Online (Sandbox Code Playgroud)
使用现场
fun useProducer(star: Producer<*>) {
// Even though we use * here, T is known to be at least a Pet
// because it's an upper bound at the declaration site.
// So, Pet is guaranteed.
val pet = star.produce() // OK
// Can use properties and functions of Pet.
pet.cutenessIndex // OK
}
Run Code Online (Sandbox Code Playgroud)
申报现场
class Consumer<in T> {
private val items = mutableListOf<T>()
fun consume(item: T) = items.add(item)
fun size(): Int = items.size
}
Run Code Online (Sandbox Code Playgroud)
使用现场
fun useConsumer(consumer: Consumer<*>) {
// Cannot consume anything because
// lower bound is not supported in Kotlin and T is unknown.
consumer.consume(Pet()) // Error
// Can use T-independent functions and properties.
consumer.size() // OK
}
Run Code Online (Sandbox Code Playgroud)
Kotlin 不支持下限。因此,在Consumer上面的类中,我们不能有像in Pet : T(上界)那样的out T : Pet(下界)。众所周知,消费者可以消费T及其子类型。Nothing是 Kotlin 中所有类型的子类型,就像Any?是所有类型的超类型一样。由于T在星投影中未知,唯一已知的子类型T是Nothing。这就是为什么一个消费者的星投影只能消费Nothing。因此,说Consumer<*>与说是同一件事Consumer<in Nothing>。
就是这样!希望这有助于解决问题。
这也在 Kotlin文档中进行了解释:
对于
Foo<in T>,其中T是逆变类型参数,Foo<*>等效于Foo<in Nothing>。这意味着当未知时,您无法以Foo<*>安全的方式写入任何内容T。
我们有一个Foo<T>具有逆变T(声明站点)的类,即Foo仅作为 的消费者工作T:
class Foo<in T> {
fun accept(t: T) {
println(t)
}
}
Run Code Online (Sandbox Code Playgroud)
我们可以在简单的泛型函数中使用这种类型,如下所示:
fun <F> usingFoo(con: Foo<F>, t: F) {
con.accept(t)
}
Run Code Online (Sandbox Code Playgroud)
(用于F区分类类型参数T)
这很好用Foo,因为我们按预期使用:作为Ts的消费者。
现在,您的引述只是说使用 typecon: Foo<*> 而不是参数con: Foo<T>会产生以下效果:“您不能调用T签名中具有的任何方法”。
因此,以下失败:
fun <F> usingFoo(con: Foo<*>, t: F) {
con.accept(t) //not allowed!!
}
Run Code Online (Sandbox Code Playgroud)
accept使用 的实例调用是不安全的,F因为我们不知道的类型Foo(由星形投影表示)。
Star Projection:有时你想说你对类型参数一无所知,但仍然想以安全的方式使用它。这里的安全方法是定义泛型类型的这种投影,该泛型类型的每个具体实例都将是该投影的子类型。
| 归档时间: |
|
| 查看次数: |
1198 次 |
| 最近记录: |