我一直在学习 Kotlin 中的 in 和 out 修饰符。 从 Java 的角度来看,文档强化了从 Java 中映射概念的方法,我不相信这是最好的方法,因为我和其他人很难理解它。 像 fun computeSomething(List<out T>) 这样的声明的 in 和 out 有点像 Java 的 <? 超级 T> 和 <? extends T> 但它们并不完全相同,我认为这是因为尽管 Kotlin 编译器最终将它们映射到 Java 可用的相同 JVM 概念,但它也进行了自己的编译时检查,这并不完全是 相同的。
与其尝试将它们映射到 Java,并且在较高级别上,我认为这就是这些修饰符的含义。 我写这篇文章的部分原因是为了更好地理解它,所以如果我没有触及到每一个细微差别,请多多包涵,如果我有什么问题,请告诉我。
out :我要从这里提取一个值。 它是由这段代码产生的。 这对于计算结果的类和函数很有用。
interface Producer<out T> {
fun produce(): T
}
我可以将 Producer<String> 值分配给 Producer<Any> 对象,因为 producer() 将提取出可以分配给 Any 对象的值。
in:我要在这里发送一个值。 它被这段代码消耗掉了。 当您读取对象的属性并计算其他内容并且您不存储或创建这些对象时,这很有用。
interface Consumer<in T> {
fun consume(thing: T)
}
Comparable 实现只是读入一个 T 而没有做一个。
使用上面的接口:
class IntProducer: Producer<Int> {
override fun produce() = (Math.random() * 1000).toInt()
}class Printer<in T>: Consumer<T> {
override fun consume(thing: T) {
println(thing)
}
}fun inOutTest() {
val numberProducer: Producer<Number> = IntProducer()
val numberPrinter = Printer<Number>()
numberPrinter.consume(numberProducer.produce())
}
但是,此代码无法编译:
interface NumberProducer: Producer<Number>fun fullOfErrors() {
val numberProducer: NumberProducer = IntProducer() // wrong type
}
in 和 out 工作方式的一个有趣结果是 out 往往适用于不可变集合,而似乎两者都不适用于也会产生结果的可变集合。
看看这段代码:
interface ImmutableStack<out T> {
fun peek(): T
}
interface GrowingStack<in T> {
fun push(item: T)
}
interface ShrinkingStack<out T> {
fun pop(): T
}
class Stack<T>: ImmutableStack<T>, GrowingStack<T>, ShrinkingStack<T> {
private val items: MutableList<T>
constructor(items: Iterable<T>) {
this.items = items.toMutableList()
}
override fun peek() = items.last()
override fun push(item: T) {
items.add(item)
}
override fun pop(): T = items.removeLast()
}
为 GrowingStack 和 ShrinkingStack 设置单独的接口看起来有点荒谬,但如果我尝试将它们结合起来,那么我不能在同一个接口或类中同时使用 push() 和 pop(),除非我同时删除了 in 和 out。
下面的代码将导致错误类型参数 T 被声明为 'out' 但在 push() 上类型 T 中的 'in' 位置出现,直到我删除 out,如果我将其替换为 in,则相反的情况发生在 其他功能。
class MutableStack<out T>: ImmutableStack<T> {
private val items: MutableList<T>
constructor(items: Iterable<T>) {
this.items = items.toMutableList()
}
override fun peek(): T = items.last()
fun push(item: T) = items.add(item)
fun pop(): T = items.removeLast()
}
我想这意味着所有可变集合类都具有既不入也不出的类型参数。 这可能会限制编译器可以提供的帮助。 但是,请继续使用那些不可变的集合,以便您可以从某种类型的 Collection<Int> 中返回一个 Int ,其中需要一个 Number 。