TL; DR:
您的Pets类可以通过返回成员变量来产生类型A的值pet,因此
aPet[VeryGeneral]
不能是它的子类型Pet[VerySpecial]
,因为当它产生某种东西时VeryGeneral,它不能保证它也是VerySpecial
。因此,它不可能是互变的。您的Pets类可以通过将类型的值作为参数传递给来使用类型A的值add。因此
aPet[VerySpecial]
不能是pet的子类型Pet[VeryGeneral]
,因为它会阻塞任何非pet的输入VerySpecial
。因此,您的课程不能是协变的。
唯一剩下的可能性是:Pets必须在中不变A。
插图:协方差与逆方差:
我将利用这个机会来介绍此漫画的改进版本和更加严格的版本。它说明了 带有子类型和声明站点差异注释的编程语言的协方差和矛盾关系概念(显然,即使Java人员也发现它足够启发人,尽管事实是有关使用站点差异的问题)。
首先,插图:
协方差-反方差漫画
现在使用可编译的Scala代码进行更详细的描述。
协方差的说明(图1的左侧)
考虑以下从非常普遍到非常具体的能源层次:
class EnergySourceclass Vegetables extends EnergySourceclass Bamboo extends Vegetables
现在考虑Consumer[-A]具有单个consume(a: A)方法的特征:
trait Consumer[-A] { def consume(a: A): Unit}
让我们实现此特征的一些示例:
object Fire extends Consumer[EnergySource] { def consume(a: EnergySource): Unit = a match { case b: Bamboo => println("That's bamboo! Burn, bamboo!") case v: Vegetables => println("Water evaporates, vegetable burns.") case c: EnergySource => println("A generic energy source. It burns.") }}object GeneralistHerbivore extends Consumer[Vegetables] { def consume(a: Vegetables): Unit = a match { case b: Bamboo => println("Fresh bamboo shoots, delicious!") case v: Vegetables => println("Some vegetables, nice.") }}object Panda extends Consumer[Bamboo] { def consume(b: Bamboo): Unit = println("Bamboo! I eat nothing else!")}
现在,为什么Consumer必须要变调A?让我们尝试实例化一些不同的能源,然后将它们提供给各个消费者:
val oilBarrel = new EnergySourceval mixedVegetables = new Vegetablesval bamboo = new BambooFire.consume(bamboo) // okFire.consume(mixedVegetables) // okFire.consume(oilBarrel) // okGeneralistHerbivore.consume(bamboo)// okGeneralistHerbivore.consume(mixedVegetables) // ok// GeneralistHerbivore.consume(oilBarrel) // No! Won't compilePanda.consume(bamboo) // ok// Panda.consume(mixedVegetables) // No! Might contain sth Panda is allergic to// Panda.consume(oilBarrel) // No! Pandas obviously cannot eat crude oil
结果是:Fire可以消耗一个GeneralistHerbivore罐头GeneralistHerbivore所消耗的一切,进而可以消耗一个Panda罐头所消耗的一切。因此,只要我们只在乎消耗能源的能力, Consumer[EnergySource]就可以在Consumer[Vegetables]需要a的地方替代,而 Consumer[Vegetables]在Consumer[Bamboo]需要a的地方替代。因此,即使类型参数之间的关系完全相反,也可以认为
Consumer[EnergySource] <: Consumer[Vegetables]和
Consumer[Vegetables] <: Consumer[Bamboo]完全相反:
type >:>[B, A] = A <:< Bimplicitly: EnergySource >:> Vegetablesimplicitly: EnergySource >:> Bambooimplicitly: Vegetables >:> Bambooimplicitly: Consumer[EnergySource] <:< Consumer[Vegetables]implicitly: Consumer[EnergySource] <:< Consumer[Bamboo]implicitly: Consumer[Vegetables] <:< Consumer[Bamboo]
协方差的说明(图1的右侧)
定义产品的层次结构:
class Entertainmentclass Music extends Entertainmentclass metal extends Music // yes, it does, seriously^^
定义可以产生类型值的特征A:
trait Producer[+A] { def get: A}
定义不同专业水平的各种“来源” /“生产者”:
object BrowseYoutube extends Producer[Entertainment] { def get: Entertainment = List( new Entertainment { override def toString = "Lolcats" }, new Entertainment { override def toString = "Juggling Clowns" }, new Music { override def toString = "Rick Astley" } )((System.currentTimeMillis % 3).toInt)}object RandomMusician extends Producer[Music] { def get: Music = List( new Music { override def toString = "...plays Mozart's Piano Sonata no. 11" }, new Music { override def toString = "...plays BBF3 piano cover" } )((System.currentTimeMillis % 2).toInt)}object metalBandMember extends Producer[metal] { def get = new metal { override def toString = "I" }}
该BrowseYoutube是最普通的来源Entertainment:它可以给你基本上任何一种娱乐:猫视频,杂耍小丑,或(意外),一些音乐。Entertainment图1中的典型小丑代表了这种一般来源。
的RandomMusician已经有些专业化了,至少我们知道这个对象会产生音乐(即使对任何特定类型都没有限制)。
最后,metalBandMember它非常专业:get保证该方法仅返回非常特定类型的metal音乐。
让我们尝试Entertainment从这三个对象中获取各种:
val entertainment1: Entertainment = BrowseYoutube.get // okval entertainment2: Entertainment = RandomMusician.get // okval entertainment3: Entertainment = metalBandMember.get // ok// val music1: Music = BrowseYoutube.get // No: could be cat videos!val music2: Music = RandomMusician.get // okval music3: Music = metalBandMember.get // ok// val metal1: Entertainment = BrowseYoutube.get // No, probably not even music// val metal2: Entertainment = RandomMusician.get // No, could be Mozart, could be Rick Astleyval metal3: Entertainment = metalBandMember.get // ok, because we get it from the specialist
我们看到这三个
Producer[Entertainment],
Producer[Music]并且
Producer[metal]可以产生某种
Entertainment。我们只看到这一点,
Producer[Music]并
Producer[metal]保证会产生
Music。最后,我们看到只有极专业的人才
Producer[metal]可以保证生产,metal而没有别的。因此,Producer[Music]与Producer[metal]可以取代一个
Producer[Entertainment]。
AProducer[metal]可以代替
Producer[Music]。通常,可以将特定产品种类的生产者替换为较不专业的生产者:
implicitly: metal <:< Musicimplicitly: metal<:< Entertainmentimplicitly: Music <:< Entertainmentimplicitly: Producer[metal] <:< Producer[Music]implicitly: Producer[metal] <:< Producer[Entertainment]implicitly: Producer[Music] <:< Producer[Entertainment]
产品之间的子类型关系与产品生产者之间的子类型关系相同。这就是协方差的含义。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)