Scala泛型不变性疑问:不变Stack为何可存入Fruit子类?
理解Scala泛型不变性与元素存入的关系
我当初学Scala泛型的时候也踩过这个坑,把「元素的类型兼容性」和「泛型容器的方差」这俩概念搞混了,咱们掰开揉碎说清楚:
为什么你的Stack[Fruit]能存Apple和Banana?
你写的代码里,Stack[Fruit]的push方法参数类型是Fruit对吧?而Apple和Banana都是Fruit的子类,根据面向对象的多态特性,子类实例可以自动向上转型为父类类型——也就是说new Apple()本质上可以被当作Fruit来用,所以push方法自然能接受它们。这和泛型的不变性半毛钱关系都没有,纯粹是普通的子类向上兼容。
举个更直白的例子:你定义一个def eat(f: Fruit): Unit方法,同样能传Apple或Banana进去,这和泛型无关,就是基础的多态逻辑。
那泛型协变(+A)是用来干嘛的?
泛型方差管的是容器类型本身的子类型关系,而不是容器里能装什么元素。
- 当Stack是不变的(默认情况,比如
class Stack[A]),Stack[Apple]和Stack[Fruit]之间没有任何子类型关系——你不能把Stack[Apple]赋值给Stack[Fruit]类型的变量,比如:val appleStack: Stack[Apple] = new Stack[Apple] val fruitStack: Stack[Fruit] = appleStack // 编译错误!因为不变的泛型不允许这样的转换 - 但如果Stack是协变的(
class Stack[+A]),那Stack[Apple]就可以被当作Stack[Fruit]的子类型来用,上面的赋值就会编译通过。这时候你才能说「Stack[Apple]是Stack[Fruit]的一种」,而不是说「Stack[Fruit]能装Apple」——后者本来就能做到。
一句话总结
- 不变的泛型容器:容器类型之间没有子类型关系,但容器里可以装任何符合元素类型的子类实例(靠多态)。
- 协变的泛型容器:不仅能装子类元素,容器类型本身也能向上兼容(比如
Stack[Apple]→Stack[Fruit])。
内容的提问来源于stack exchange,提问作者vijayinani
相关产品推荐
相关产品推荐

