我不完全理解泛型中的方差是如何工作的。在下面的代码中,类如下Any -> Mammals -> Cats
。Any
是超类型,在copy function
中有一个名为from
的参数
根据我对out
和in
关键字的理解,out
允许引用它的任何subtype
,只能生成而不能消费。
in
允许引用它的任何一个supertype
,只能消费而不能生产。
然而,在copytest function
中,我们正在实例化函数copy
。我在from
参数中给了它一个catlist1
参数。既然参数有一个out
关键字,这不是意味着我们只能输入catlist2
的subtype
参数吗?
更让我困惑的是,我看到了许多相互冲突的定义,例如,在Kotlin中,我们可以在泛型类型上使用out
关键字,这意味着我们可以将此引用分配给它的任何超类型。
现在我真的很困惑,有人能指导我这些是如何运作的吗?最好从头开始,谢谢!
class list2<ITEM>{
val data = mutableListOf<ITEM>()
fun get(n:Int):ITEM = data[n]
fun add(Item:ITEM){data.add(Item)}
}
fun <T> Copy(from: list2<out T>, to:list2<T>){
}
fun copytest(){
val catlist1 = list2<Cat>()
val catlist2 = list2<Cat>()
val mammallist = list2<Mammal>()
Copy(catlist1,mammallist)
}
我认为您可能混淆了类声明站点泛型和使用站点泛型。
类声明站点泛型
在类声明站点使用协变out
定义,确实不能将泛型类型用作类中任何函数的函数参数类型。
class MyList<out T>(
private val items: Array<T>
) {
fun pullRandomItem(): T { // allowed
return items.random()
}
fun addItem(item: T) { // Not allowed by compiler!
// ...
}
}
// Reason:
val cowList = MyList<Cow>(arrayOf(Cow()))
// The declaration site out covariance allows us to up-cast to a more general type.
// It makes logical sense, any cow you pull out of the original list qualifies as an animal.
val animalList: MyList<Animal> = cowList
// If it let us put an item in, though:
animalList.addItem(Horse())
// Now there's a horse in the cow list. That doesn't make logical sense
cowList.pullRandomItem() // Might return a Horse, impossible!
说";我将把一匹马放在一个列表中,该列表可能要求从中检索到的所有物品都必须是奶牛">
使用站点泛型
这与类级别限制无关。它只是描述函数得到什么样的输入。说";我的函数对一个容器做了一些事情,我要从中取出一些东西;。
// Given a class with no declaration-site covariance of contravariance:
class Bag<T: Any>(var contents: T?)
// This function will take any bag of food as a parameter. Inside the function, it will
// only get things out of the bag. It won't put things in it. This makes it possible
// to pass a Bag of Chips or a Bag of Pretzels
fun eatBagContents(bagOfAnything: Bag<out Food>) {
eat(bagOfAnything.contents) // we know the contents are food so this is OK
bagOfAnything.contents = myChips // Not allowed! we don't know what kind of stuff
// this bag is permitted to contain
}
// If we didn't define the function with "out"
fun eatBagContentsAndPutInSomething(bagOfAnything: Bag<Food>) {
eat(bagOfAnything.contents) // this is fine, we know it's food
bagOfAnything.contents = myChips // this is fine, the bag can hold any kind of Food
}
// but now you cannot do this
val myBagOfPretzels: Bag<Pretzels> = Bag(somePretzels)
eatBagContentsAndPutInSomething(myBagOfPretzels) // Not allowed! This function would
// try to put chips in this pretzels-only bag.
组合两者
如果您看到一个将以上两者结合在一起的示例,您可能会感到困惑。您可以有一个类,其中T
是一个声明站点类型,但该类具有一些函数,其中有输入参数,T
是函数可以采用的参数定义的一部分。例如:
abstract class ComplicatedCopier<T> {
abstract fun createCopy(item: T): T
fun createCopiesFromBagToAnother(copyFrom: Bag<out T>, copyTo: Bag<in T>) {
val originalItem = copyFrom.contents
val copiedItem = createCopy(originalItem)
copyTo.contents = copiedItem
}
}
这在逻辑上是有意义的,因为类泛型类型在声明位置没有差异限制。该功能有一个袋子,可以取出物品,还有一个袋子可以放入物品。这些in
和out
关键字使它更允许您传递给它什么类型的袋子,但它限制了您在函数内对每个袋子所做的操作。