Generics kotlin中的out关键字是什么
我无法理解,也无法在kotlin中找到out关键字的含义 您可以在此处查看示例:Generics kotlin中的out关键字是什么,generics,kotlin,Generics,Kotlin,我无法理解,也无法在kotlin中找到out关键字的含义 您可以在此处查看示例: List<out T> 列表 如果有人能解释这句话的意思。非常感谢。请参阅 Kotlin列表类型是一个提供只读的接口 像size、get等操作。与Java一样,它继承自 集合,而该集合又继承自Iterable。方法 通过MutableList界面添加更改列表。这 模式也适用于Set/MutableSet和Map/MutableMap 而这个, 在科特林,有一种方法可以向公众解释这类事情 编译器。这称
List<out T>
列表
如果有人能解释这句话的意思。非常感谢。请参阅
Kotlin列表
类型是一个提供只读的接口
像size、get等操作。与Java一样,它继承自
集合
,而该集合又继承自Iterable
。方法
通过MutableList
界面添加更改列表。这
模式也适用于Set/MutableSet
和Map/MutableMap
而这个,
在科特林,有一种方法可以向公众解释这类事情
编译器。这称为声明站点差异:我们可以注释
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
> // ... }
>抽象类源代码{
>抽象趣味nextT():T}
>
>趣味演示(strs:来源){
>val objects:Source=strs//这是可以的,因为T是一个out参数
> // ... }
一般规则是:当声明类C
的类型参数T
时
out,它可能只出现在C
的成员中的out位置,但在
returnC
可以安全地成为C
的超类型
在“聪明的话”中,他们说类C
在
参数T
,或者T
是协变类型参数。你可以想到
C作为T的生产者,而不是T的消费者。
out修饰符称为方差注释,因为它是
在类型参数声明站点提供,我们讨论
申报地点差异。这与Java的使用站点形成对比
方差,其中类型用法中的通配符使类型协变
签名如下:
List<out T>
因此,当需要列表
时,可以安全地提供列表
,因此列表
是列表
的超级类型,但反之亦然
List<out T> is like List<? extends T> in Java
在可比的示例中:
因此,当需要compariable
时,可以安全地提供compariable
,因此compariable
是compariable
的超级类型,但反之亦然。列表类似于列表请记住:
List<out T> is like List<? extends T> in Java
in
是“forinput”-你想把(写)东西放进去(所以它是“消费者”)
out
是“foroutput”-你想从中获取(阅读)一些东西(因此它是“制作人”)
如果你来自爪哇
是用于输入的,所以它就像方差修饰符out
和in
允许我们通过允许子类型来减少泛型类型的限制性和可重用性
让我们通过对比的例子来理解这一点。我们将使用案例作为各种武器的容器。假设我们有以下类型层次结构:
open class Weapon
open class Rifle : Weapon()
class SniperRifle : Rifle()
out
生成T
并保留子类型
当您使用out
修饰符声明泛型类型时,它被称为协变。协变变量是T
的生产者,这意味着函数可以返回T
,但不能将T
作为参数:
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
是Case
的子类型,而sniperifle
是Rifle
的子类型。因此,useProducer()
函数也可以通过Case
调用:
useProducer(Case<SniperRifle>()) // OK
useProducer(Case<Rifle>) // OK
useProducer(Case<Weapon>()) // Error
useConsumer(Case<SniperRifle>()) // Error
useConsumer(Case<Rifle>()) // OK
useConsumer(Case<Weapon>()) // OK
用
中的
修饰符声明的
案例
使用
T及其子类型:
fun useProducer(case: Case<Rifle>) {
// Produces Rifle and its subtypes
val rifle = case.produce()
}
fun useConsumer(case: Case<Rifle>) {
// Consumes Rifle and its subtypes
case.consume(SniperRifle())
}
fun useProducerConsumer(case: Case<Rifle>) {
// Produces Rifle and its subtypes
case.produce()
// Consumes Rifle and its subtypes
case.consume(SniperRifle())
}
这在消费时限制较少,并且更易于重用,但我们的类变成了只写类
不变量产生并使用
T
,不允许子类型
当您在没有任何方差修饰符的情况下声明泛型类型时,它被称为不变量。不变量是T
的生产者和消费者,这意味着函数可以将T
作为参数,也可以返回T
:
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
}
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
和Case
都不是Case
的子类型。因此,useProducerConsumer()
函数只能在使用Case
的情况下调用:
useProducerConsumer(Case())//错误
useProducerConsumer(Case())//确定
useProducerConsumer(Case())//错误
这在生产和消费时限制性更强,可重用性更低,但我们可以读写
结论 Kotlin中的
列表
仅为生产者。因为它是使用out
修饰符声明的:List
。这意味着您不能向它添加元素,因为add(element:T)
是一个消费函数。每当您希望能够get()
以及add()
元素时,请使用不变版本MutableList
就这样!希望这有助于理解方差的
in
s和out
s 这些答案解释了输出
的功能,但不解释为什么你需要它,所以让我们假装我们根本没有输出。想象三个类: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 allSpeak(animals: List<Animal>) {
animals.forEach { it.speak() }
}
fun main() {
val dogs: List<Dog> = listOf(Dog(), Dog())
allSpeak(dogs)
val mixed: List<Animal> = listOf(Dog(), Cat())
allSpeak(mixed)
}
你不能安全地认为<代码> MutableList <代码>是一个子类型:<代码> MutableList <代码>,因为你可以对后者做些事情(插入一个猫),这是你不能做的。
fun useConsumer(case: Case<Rifle>) {
// Consumes Rifle and its subtypes
case.consume(SniperRifle())
}
useConsumer(Case<SniperRifle>()) // Error
useConsumer(Case<Rifle>()) // OK
useConsumer(Case<Weapon>()) // OK
class Case<T> {
private val contents = mutableListOf<T>()
fun produce(): T = contents.last() // Producer: OK
fun consume(item: T) = contents.add(item) // Consumer: OK
}
fun useProducerConsumer(case: Case<Rifle>) {
// Produces Rifle and its subtypes
case.produce()
// Consumes Rifle and its subtypes
case.consume(SniperRifle())
}
useProducerConsumer(Case<SniperRifle>()) // Error
useProducerConsumer(Case<Rifle>()) // OK
useProducerConsumer(Case<Weapon>()) // Error
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 allSpeak(animals: List<Animal>) {
animals.forEach { it.speak() }
}
fun main() {
val dogs: List<Dog> = listOf(Dog(), Dog())
allSpeak(dogs)
val mixed: List<Animal> = listOf(Dog(), Cat())
allSpeak(mixed)
}
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")