fre*_*oma 51 arrays scala list covariance
例如,为什么呢
val list:List[Any] = List[Int](1,2,3)
Run Code Online (Sandbox Code Playgroud)
工作,但是
val arr:Array[Any] = Array[Int](1,2,3)
Run Code Online (Sandbox Code Playgroud)
失败(因为数组是不变的).这个设计决定背后的效果是什么?
Op *_*kel 73
因为它会破坏类型安全性.如果没有,你可以做这样的事情:
val arr:Array[Int] = Array[Int](1,2,3)
val arr2:Array[Any] = arr
arr2(0) = 2.54
Run Code Online (Sandbox Code Playgroud)
并且编译器无法捕获它.
另一方面,列表是不可变的,因此您无法添加不是的内容 Int
ssh*_*nin 26
这是因为列表是不可变的,并且数组是可变的.
给出的正常答案是可变性与协方差相结合会破坏类型安全。对于集合,这可以被视为一个基本真理。但该理论实际上适用于任何泛型类型,而不仅仅是像ListandArray这样的集合,我们根本不必尝试和推理可变性。
真正的答案与函数类型与子类型交互的方式有关。简而言之,如果将类型参数用作返回类型,则它是协变的。另一方面,如果将类型参数用作参数类型,则它是逆变的。如果它既用作返回类型又用作参数类型,则它是不变的。
让我们看看文档Array[T]。要查看的两个明显方法是用于查找和更新的方法:
def apply(i: Int): T
def update(i: Int, x: T): Unit
Run Code Online (Sandbox Code Playgroud)
第一种方法T是返回类型,而第二种方法T是参数类型。方差规则规定,T因此必须是不变的。
我们可以比较文档List[A]以了解它为什么是协变的。令人困惑的是,我们会发现这些方法类似于以下方法Array[T]:
def apply(n: Int): A
def ::(x: A): List[A]
Run Code Online (Sandbox Code Playgroud)
由于A既用作返回类型又用作参数类型,我们希望A像Tfor一样保持不变Array[T]。但是,与 不同Array[T],文档在::. 对于大多数调用此方法而言,谎言已经足够好,但不足以确定 的方差A。如果我们展开此方法的文档并单击“完整签名”,我们就会看到真相:
def ::[B >: A](x: B): List[B]
Run Code Online (Sandbox Code Playgroud)
所以A实际上并不作为参数类型出现。相反,B(可以是 的任何超类型A)是参数类型。这对 没有任何限制A,因此它确实可以是协变的。任何将List[A]其A作为参数类型的方法都是一个类似的谎言(我们可以判断,因为这些方法被标记为[use case])。
区别在于Lists是不可变的,而Arrays是可变的.
要理解为什么可变性决定方差,考虑制作一个可变版本List- 让我们称之为MutableList.我们还会使用一些示例类型:一个基类Animal和2子命名Cat和Dog.
trait Animal {
def makeSound: String
}
class Cat extends Animal {
def makeSound = "meow"
def jump = // ...
}
class Dog extends Animal {
def makeSound = "bark"
}
Run Code Online (Sandbox Code Playgroud)
请注意,Cat还有一个方法(jump)比Dog.
然后,定义一个接受可变动物列表并修改列表的函数:
def mindlessFunc(xs: MutableList[Animal]) = {
xs += new Dog()
}
Run Code Online (Sandbox Code Playgroud)
现在,如果你将一系列猫传递给函数,会发生可怕的事情:
val cats = MutableList[Cat](cat1, cat2)
val horror = mindlessFunc(cats)
Run Code Online (Sandbox Code Playgroud)
如果我们使用粗心的编程语言,那么在编译期间将忽略它.然而,如果我们只使用以下代码访问猫的列表,我们的世界将不会崩溃:
cats.foreach(c => c.makeSound)
Run Code Online (Sandbox Code Playgroud)
但是如果我们这样做:
cats.foreach(c => c.jump)
Run Code Online (Sandbox Code Playgroud)
将发生运行时错误.使用Scala,可以防止编写此类代码,因为编译器会抱怨.
| 归档时间: |
|
| 查看次数: |
8161 次 |
| 最近记录: |