我无法理解,我无法在kotlin中找到out关键字的含义.
您可以在此查看示例:
List<out T>
Run Code Online (Sandbox Code Playgroud)
如果有人能够解释这个的含义.真的很感激.
Dmi*_*din 53
List<out T> is like List<? extends T> in Java
Run Code Online (Sandbox Code Playgroud)
和
List<in T> is like List<? super T> in Java
Run Code Online (Sandbox Code Playgroud)
例如在Kotlin你可以做类似的事情
val value : List<Any> = listOf(1,2,3)
//since List signature is List<out T> in Kotlin
Run Code Online (Sandbox Code Playgroud)
Yog*_*ity 51
方差改性剂out和in允许我们做出允许亚型我们的泛型类型限制更少,更可重复使用。
让我们借助对比示例来理解这一点。我们将使用案例作为各种武器的容器。假设我们有以下类型层次结构:
open class Weapon
open class Rifle : Weapon()
class SniperRifle : Rifle()
Run Code Online (Sandbox Code Playgroud)
out产生T并保留子类型当您使用out修饰符声明泛型类型时,它被称为covariant。协变是一个制片人的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
}
Run Code Online (Sandbox Code Playgroud)
在Case与声明的out修改产生T及其亚型:
fun useProducer(case: Case<Rifle>) {
// Produces Rifle and its subtypes
val rifle = case.produce()
}
Run Code Online (Sandbox Code Playgroud)
使用out修饰符,子类型被保留,所以Case<SniperRifle>is 的子类型Case<Rifle>whenSniperRifle是 的子类型Rifle。因此,该useProducer()函数也可以调用Case<SniperRifle>:
useProducer(Case<SniperRifle>()) // OK
useProducer(Case<Rifle>) // OK
useProducer(Case<Weapon>()) // Error
Run Code Online (Sandbox Code Playgroud)
这在生产时限制较少且可重用,但我们的类变为只读。
in消耗T和反转子类型当您使用in修饰符声明泛型类型时,它被称为contravariant. 一个逆变是一个消费的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
}
Run Code Online (Sandbox Code Playgroud)
在Case与声明的in修改消耗T及其亚型:
fun useConsumer(case: Case<Rifle>) {
// Consumes Rifle and its subtypes
case.consume(SniperRifle())
}
Run Code Online (Sandbox Code Playgroud)
使用in修饰符,子类型被反转,所以现在Case<Weapon>is a subtype Case<Rifle>when Rifleis a subtype of Weapon。因此,该useConsumer()函数也可以调用Case<Weapon>:
useConsumer(Case<SniperRifle>()) // Error
useConsumer(Case<Rifle>()) // OK
useConsumer(Case<Weapon>()) // OK
Run Code Online (Sandbox Code Playgroud)
这是限制较少和更加可重复使用而消耗,但我们班变成只写。
T,禁止子类型化当您声明一个没有任何变化修饰符的泛型类型时,它被称为invariant。不变量是 的生产者和消费者T,这意味着函数可以T作为参数,也可以返回T:
class Case<T> {
private val contents = mutableListOf<T>()
fun produce(): T = contents.last() // Producer: OK
fun consume(item: T) = contents.add(item) // Consumer: OK
}
Run Code Online (Sandbox Code Playgroud)
在Case无申报in或out修改生产和消费T及其亚型:
fun useProducerConsumer(case: Case<Rifle>) {
// Produces Rifle and its subtypes
case.produce()
// Consumes Rifle and its subtypes
case.consume(SniperRifle())
}
Run Code Online (Sandbox Code Playgroud)
没有inorout修饰符,子类型是不允许的,所以现在既不是Case<Weapon>也不Case<SniperRifle>是 的子类型Case<Rifle>。因此,useProducerConsumer()只能使用以下命令调用该函数Case<Rifle>:
useProducerConsumer(Case<SniperRifle>()) // Error
useProducerConsumer(Case<Rifle>()) // OK
useProducerConsumer(Case<Weapon>()) // Error
Run Code Online (Sandbox Code Playgroud)
这在生产和消费时更具限制性和更少的可重用性,但我们可以读取和写入.
该List在科特林只是一个生产商。因为它是使用out修饰符声明的:List<out T>。这意味着您不能向其中添加元素,因为它add(element: T)是一个使用者函数。无论何时您希望能够get()和add()元素一样,请使用不变版本MutableList<T>。
就是这样!希望这有助于理解方差的ins 和outs!
And*_*eng 43
有这个签名:
List<out T>
Run Code Online (Sandbox Code Playgroud)
你可以这样做:
val doubleList: List<Double> = listOf(1.0, 2.0)
val numberList: List<Number> = doubleList
Run Code Online (Sandbox Code Playgroud)
这意味着T是协变的:
当类型参数Ť一类Ç被声明出来,ç<数据库>可以安全地是一个超型的ç<派生>.
这是相反的,如
Comparable<in T>
Run Code Online (Sandbox Code Playgroud)
你可以这样做:
fun foo(numberComparable: Comparable<Number>) {
val doubleComparable: Comparable<Double> = numberComparable
// ...
}
Run Code Online (Sandbox Code Playgroud)
这意味着T是逆变的:
当类型参数Ť一类Ç被声明在,ç<派生>可以安全地是一个超型的ç<数据库>.
记住它的另一种方法:
消费者在,制作出来.
----------------- 2019年1月4日更新-----------------
对于" Consumer in,Producer out ",我们只读取Producer - call方法来获取类型T的结果; 并且只通过传入类型T的参数来写入Consumer - call方法.
在示例中List<out T>,很明显我们可以这样做:
val n1: Number = numberList[0]
val n2: Number = doubleList[0]
Run Code Online (Sandbox Code Playgroud)
因此,List<Double>在List<Number>预期时提供是安全的,因此List<Number>是超类型List<Double>,但反之亦然.
在示例中Comparable<in T>:
val double: Double = 1.0
doubleComparable.compareTo(double)
numberComparable.compareTo(double)
Run Code Online (Sandbox Code Playgroud)
因此,Comparable<Number>在Comparable<Double>预期时提供是安全的,因此Comparable<Double>是超类型Comparable<Number>,但反之亦然.
Rya*_*ing 14
这些答案解释了什么 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") }
}
Run Code Online (Sandbox Code Playgroud)
由于 aDog是 的子类型Animal,我们希望将其List<Dog>用作子类型,List<Animal>这意味着我们希望能够做到这一点:
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)
}
Run Code Online (Sandbox Code Playgroud)
没关系,代码将为woof woof狗和woof meow混合列表打印。
问题是当我们有一个可变容器时。由于 aList<Animal>可以包含Dogand Cat,我们可以将其中一个添加到 aMutableList<Animal>
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?
}
Run Code Online (Sandbox Code Playgroud)
您不能安全地将MutableList<Dog>其视为 的子类型,MutableList<Animal>因为您可以对后者做一些您不能对前者做的事情(插入一只猫)。
举个更极端的例子:
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")
Run Code Online (Sandbox Code Playgroud)
问题仅在添加到列表时发生,而不是从中读取。使用List<Dog>as是安全的,List<Animal>因为您不能附加到List. 这就是out告诉我们的。out说“这是我输出的类型,但我不把它作为我消费的新输入”
像这样记住:
in是“for in put”——你想把(写)一些东西放进去(所以它是一个“消费者”)
out是“为了出去” - 你想从中取出(读取)一些东西(所以它是一个“生产者”)
如果你来自爪哇,
<in T>用于输入,所以它就像<? super T>(消费者)
<out T>用于输出,所以就像<? extends T>(生产者)
生产=输出=输出。
\ninterface Production<out T> {\n fun produce(): T\n}\nRun Code Online (Sandbox Code Playgroud)\n如果您的泛型类仅使用泛型类型作为 it\xe2\x80\x99s 函数的输出,则使用 out
\n消耗=输入=输入。
\n interface Consumer<in T> {\n fun consume(item: T)\n}\nRun Code Online (Sandbox Code Playgroud)\n如果您的泛型类仅使用泛型类型作为 it\xe2\x80\x99s 函数的输入,则使用 in
\n请参阅\n https://medium.com/mobile-app-development-publication/in-and-out-type-variant-of-kotlin-587e4fa2944c\n了解详细说明。
\n请参阅kotlin的手册
Kotlin
List<out T>类型是一个提供只读操作的接口,如size,get等.就像在Java中一样,它继承自Collection<T>而且继承自Iterable<T>.MutableList<T>接口添加更改列表的方法.这种模式也适用于Set<out T>/MutableSet<T>和Map<K, outV>/MutableMap<K, V>
还有这个,
在Kotlin中,有一种方法可以向编译器解释这种事情.这称为声明站点方差:我们可以注释Source的类型参数T,以确保它仅从成员返回(生成)
Source<T>,并且从不消耗.为此,我们提供了out修饰符:Run Code Online (Sandbox Code Playgroud)> 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 > // ... }一般规则是:当声明
T一个类的类型参数C时,它可能只发生在成员的外部位置C,但作为回报C<Base>可以安全地成为超类型C<Derived>.在"聪明的话"中,他们说该类
C在参数中是协变的T,或者T是协变类型参数.您可以将C视为T的生产者,而不是消费者T.out修饰符称为方差注释,因为它是在类型参数声明站点提供的,所以我们讨论声明站点方差.这与Java的使用站点差异形成对比,其中类型用法中的通配符使类型变为协变.
| 归档时间: |
|
| 查看次数: |
11197 次 |
| 最近记录: |