在 Kotlin,有一种方法可以解释这类事情
这就是所谓的声明-站点方差: 我们可以注释
Source 的 type 参数 T,以确保只返回它
(生产)从 Source<T>的成员,从来没有消耗。这样做
我们提供 out 修饰符:
> abstract class Source<out T> {
> abstract fun nextT(): T }
>
> fun demo(strs: Source<String>) {
> val objects: Source<Any> = strs // This is OK, since T is an out-parameter
> // ... }
class Case<out T> {
private val contents = mutableListOf<T>()
fun produce(): T = contents.last() // Producer: OK
fun consume(item: T) = contents.add(item) // Consumer: Error
}
使用 out修饰符声明的 Case产生 T及其子类型:
fun useProducer(case: Case<Rifle>) {
// Produces Rifle and its subtypes
val rifle = case.produce()
}
class Case<in T> {
private val contents = mutableListOf<T>()
fun produce(): T = contents.last() // Producer: Error
fun consume(item: T) = contents.add(item) // Consumer: OK
}
使用 in修饰符声明的 Case消耗 T及其子类型:
fun useConsumer(case: Case<Rifle>) {
// Consumes Rifle and its subtypes
case.consume(SniperRifle())
}
class Case<T> {
private val contents = mutableListOf<T>()
fun produce(): T = contents.last() // Producer: OK
fun consume(item: T) = contents.add(item) // Consumer: OK
}
没有 in或 out修饰符的 Case产生并消耗 T及其子类型:
fun useProducerConsumer(case: Case<Rifle>) {
// Produces Rifle and its subtypes
case.produce()
// Consumes Rifle and its subtypes
case.consume(SniperRifle())
}
这些答案解释了 什么的 out,但不是 为什么你需要它,所以让我们假设我们根本没有 out。假设有三个类: Animal、 Cat、 Dog 和一个带有 Animal列表的函数
abstract class Animal {
abstract fun speak()
}
class Dog: Animal() {
fun fetch() {}
override fun speak() { println("woof") }
}
class Cat: Animal() {
fun scratch() {}
override fun speak() { println("meow") }
}
fun processAnimals(animals: MutableList<Animal>) {
animals.add(Cat()) // uh oh, what if this is a list of Dogs?
}
fun main() {
val dogs: MutableList<Dog> = mutableListOf(Dog(), Dog())
processAnimals(dogs) // we just added a Cat to a list of Dogs!
val d: Dog = dogs.last() // list of Dogs, so return type of .last() is Dog
// but this is actually a Cat
d.fetch() // a Cat can't fetch, so what should happen here?
}
val dogs: MutableList<Dog> = mutableListOf(Dog())
val anything: MutableList<Any> = dogs
// now I can add any type I want to the dogs list through the anything list
anything.add("hello world")
sealed class List<out A>
object Nil : List<Nothing>()
data class Cons<out A>(val head: A, val tail: List<A>) : List<A>()
在声明类 List 中,类型参数 A 前面的 out 是一个方差注释,表明 A 是 List 的协变量或“正”参数。这意味着,例如,假设 Dog 是 Animal 的一个子类型,List 被认为是 List 的一个子类型。(更一般地说,对于所有类型 X 和 Y,如果 X 是 Y 的子类型,则 List 是 List 的子类型。)我们可以省略 A 前面的 out,这将使 List 在该类型参数中保持不变。
但注意,现在 Nil 扩展了 List。Nothing 是所有类型的子类型,这意味着结合方差注释,Nil 可以被认为是 List、 List 等等,正如我们所希望的那样。