(Co)列表中的差异与Scala中的Stacks不同吗?

时间:2010-09-07 14:00:22

标签: java generics scala covariance

当我编写此代码时,我在Scala中遇到了编译错误

var s: Stack[_ <: Number] = new Stack[Integer]; 
s.push(new Integer(1)); //compile-error: type mismatch; found :Integer required: _$bqjyh where type _$bqjyh <: Number
s.push(null); //compile-error: type mismatch; found   : Null(null) required: _$2 where type _$2 <: Objects.Vehicle

这相当于Java中的协变集合,因为通配符;它确切的类型在未知,所以我们不能添加一些东西到堆栈。

但是对于列表,我不会得到同样的错误:

   var list: List[_ <: Number] = Nil;
   var intList : List[Integer] = new Integer(1) :: Nil;
   list = intList ; //no error
   list = new Float(2) :: vehicles;  //even float allowed

现在我甚至可以添加float,但实际上我会认为listList Integers,因此不允许Floats

1)为什么列表中允许这样,而堆栈则不允许?这是由于cons(::)运算符吗?

2)列表的类型是什么?它是动态的吗?

3)为什么在Scala中允许这样做而在Java中不允许?

4)我可以在堆栈中添加内容吗? (null不起作用,在Java中是这样的,因为泛型类型只允许引用类型)

2 个答案:

答案 0 :(得分:6)

::不是变异操作。这意味着x :: xs将返回类型List[ commonSupertypeOf[ typeOf[x], elementTypeOf[xs] ] ]的列表(这不是实际的scala代码,但我希望我的观点能够实现),但它不会更改xs的类型。如果xs的类型为List[Float]x的类型为Integer,则表达式x :: xs的类型为List[Numeric],但类型为xs 1}}仍为List[Float],因此没有任何内容。

然而,

add是一个变异操作。 xs.add(x)会将Integer添加到类型为Stack的{​​{1}},这显然是错误。

这解释了为什么做Stack<Float>并不危险。现在来解释为什么它出现了问题:

x :: xs::的绰号为:List[A]

这意味着对于任何类型def :: [B >: A] (x: B) : List[B]A,其中BB的超类型,A给定的值类型为{{ 1}}和类型::的列表将生成类型B的列表。因此,当您执行A时,编译器会推断BsomeInteger :: someFloatsBNumeric,一切正常。

在java术语中AFloat不是合法的java。

答案 1 :(得分:4)

Scala语言使用定义 - 站点差异注释,而不是使用站点差异注释。列表的协方差在列表特征中定义,而堆栈未定义为协变。通常,可变集合不能是协变的,因为这会导致类型错误,包括将新元素插入到集合中(这是Java协变数组的一个主要问题。)