当我编写此代码时,我在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
,但实际上我会认为list
是List
Integers
,因此不允许Floats
。
1)为什么列表中允许这样,而堆栈则不允许?这是由于cons(::)运算符吗?
2)列表的类型是什么?它是动态的吗?
3)为什么在Scala中允许这样做而在Java中不允许?
4)我可以在堆栈中添加内容吗? (null
不起作用,在Java中是这样的,因为泛型类型只允许引用类型)
答案 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
,其中B
是B
的超类型,A
给定的值类型为{{ 1}}和类型::
的列表将生成类型B
的列表。因此,当您执行A
时,编译器会推断B
为someInteger :: someFloats
而B
为Numeric
,一切正常。
在java术语中A
,Float
不是合法的java。
答案 1 :(得分:4)
Scala语言使用定义 - 站点差异注释,而不是使用站点差异注释。列表的协方差在列表特征中定义,而堆栈未定义为协变。通常,可变集合不能是协变的,因为这会导致类型错误,包括将新元素插入到集合中(这是Java协变数组的一个主要问题。)