2013-02-15 58 views
0

一个简单的问题。类的不可变的方法::List被定义为:::列表中的方法 - 逆变参数类型

密封抽象类列表[+ A] ...

DEF :: [B>:A](X:B):列表[B ]

假设说我有:

class Fruit 
class Mango extends Fruit 
scala> val d:List[Fruit] = List.empty[Fruit] 
d: List[Fruit] = List() 

scala> new Mango :: d 
res5: List[Fruit] = List([email protected]) 

现在我很困惑在这里。根据::声明,参数类型应该是逆变的。即在这种情况下,任何类>: Fruit(我明白为什么它是这样做的)。但是我没有得到的是Mango <: Fruit,那为什么编译器不会出错呢?

回答

3

在行

new Mango :: d 

你期望编译器的原因:“new Mango的类型为Mangod的类型为List[Fruit],所以List[Fruit].::(Mango)需要进行类型检查,而不是“。在这种情况下,这确实是一个错误。

但事实上,这方面的原因是不同的:“我需要new Mango :: d到类型检查,所以new Mango必须一些B这样B :> Fruit是否有这样的B是,B = Fruit。?。”所以没有错误。

4

您仍然能够添加Mangod即使List是不变的,只是因为Mango仍然是一个Fruit。想想看,一个普通的旧式Java列表而言:

val d = new java.util.ArrayList[Fruit] 
d.add(new Mango) 

所以逆变不限制你添加亚型的能力 - 它允许您添加超类型,并得到一个编译器检查列表从中型背:

val d: List[Fruit] = Nil 
val a: List[Plant] = new Carrot :: d 
+0

是的同意。这是'List'的属性。但是这里的方法限制了参数的类型。它应该是'_>:水果',但它的行为如同'_ <:水果' – Jatin 2013-02-15 12:16:12

+0

不,它的行为不如'_ <:Fruit'。如果是这样,'new Object :: d'将不起作用(实际上它确实)。 – 2013-02-15 13:03:29

+2

我尝试重写它:该方法需要一个'B'类型的参数,它是'A'的非严格*超类型,即'B>:A'。结果列表将具有*最具体的常见超类型*。例如。当'A = Fruit'和'B = Mango'时,你最终会得到一个List [水果];当'A = Fruit'和'B = Plant'时,你得到一个'List [Plant]'。总结一下:该方法的目的是让你建立更通用的类型容器,而不是限制你添加一个特定的子类型(顺便说一下,这是不可能的,因为每个Liskov都是可能的) – 2013-02-15 13:30:24

相关问题