Scala 中方差和可变性/不变性之间的关系 [英] relation between variance and mutabilty / immutability in Scala

查看:40
本文介绍了Scala 中方差和可变性/不变性之间的关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 Scala 中使用集合,发现可变集合被定义为不变的,而不可变的集合被定义为协变的.Scala 中的方差和可变性/不变性之间有什么关系?

I am playing around with collections in Scala and found that mutable collections are defined as invariant and immutable collections are defined as covariant. What is the relation between variance and mutability / immutability in Scala?

class Array[T]

class List[+T]

推荐答案

我在 SIA 找到了一个简单的解释.以下是直接从那里.

I had found an easy explanation in SIA. Following is straight from there.

可变对象需要保持不变当一个类型参数既不是协变也不是逆变时,它就是不变的.所有 Scala 可变集合类都是不变的.一个例子可以解释为什么可变对象需要保持不变.因为 ListBuffer 是可变的,所以它被声明为不变的,如下所示:

Mutable objects need to be invariant A type parameter is invariant when it’s neither covariant nor contravariant. All Scala mutable collection classes are invariant. An example can explain why mutable objects need to be invariant. Because ListBuffer is mutable, it’s declared as invariant as follows:

final class ListBuffer[A] ...{ ... }

因为它被声明为不变的,所以你不能将 ListBuffer 从一种类型分配给另一种类型.以下代码会抛出编译错误:

Because it’s declared as invariant, you can’t assign ListBuffer from one type to another. The following code will throw a compilation error:

scala> val mxs: ListBuffer[String] = ListBuffer("pants")
 mxs: scala.collection.mutable.ListBuffer[String] =
          ListBuffer(pants)
scala> val everything: ListBuffer[Any] = mxs
<console>:6: error: type mismatch;
found   : scala.collection.mutable.ListBuffer[String]
required: scala.collection.mutable.ListBuffer[Any]
     val everything: ListBuffer[Any] = mxs

即使 String 是 scala.Any 的一个子类型,Scala 仍然不允许您将 mxs 分配给所有内容.要理解原因,假设 ListBuffer 是协变的,并且以下代码片段没有任何编译问题:

Even though String is a subtype of scala.Any, Scala still doesn’t let you assign mxs to everything. To understand why, assume ListBuffer is covariant and the following code snippet works without any compilation problem:

scala> val mxs: ListBuffer[String] = ListBuffer("pants")
  mxs: scala.collection.mutable.ListBuffer[String] =
         ListBuffer(pants)
  scala> val everything: ListBuffer[Any] = mxs
  scala> everything += 1
  res4: everything.type = ListBuffer(1, pants)

你能发现问题吗?因为一切都是 Any 类型,所以您可以将一个整数值存储到一个字符串集合中.这是一场等待发生的灾难.这正是 Java 数组发生的事情.为了避免这些问题,让可变对象保持不变总是一个好主意.下一个问题是在集合的不可变对象的情况下会发生什么.事实证明,对于不可变对象,协方差根本不是问题.如果将 ListBuffer 替换为不可变的 List,则可以获取 List[String] 的实例并将其分配给 List[Any] 没有问题.

Can you spot the problem? Because everything is of the type Any, you can store an integer value into a collection of strings. This is a disaster waiting to happen. It’s exactly what happens to Java arrays. To avoid these kinds of problems, it’s always a good idea to make mutable objects invariant. The next question is what happens in case of an immutable object for collections. It turns out that for immutable objects, covariance isn’t a problem at all. If you replace ListBuffer with the immutable List, you can take an instance of List[String] and assign it to List[Any] with- out a problem.

scala> val xs: List[String] = List("pants")
xs: List[String] = List(pants)
scala> val everything: List[Any] = xs
everything: List[Any] = List(pants)

这个赋值安全的唯一原因是 List 是不可变的.您可以将 1 添加到 xs List,它将返回一个 Any 类型的新 List.

The only reason this assignment is safe is because List is immutable. You can add 1 to xs List, and it will return a new List of type Any.

scala> 1 :: xs
res5: List[Any] = List(1, pants)

同样,这个添加是安全的,因为 cons(::) 方法总是返回一个新的 List,它的类型由 List 中元素的类型决定.唯一可以存储整数值和引用值的类型是 scala.Any.这是在处理可变/不可变对象时要记住的关于类型变化的重要属性.

Again, this addition is safe because the cons(::) method always returns a new List, and its type is determined by the type of elements in the List. The only type that could store an integer value and reference value is scala.Any. This is an important property to remember about type variance when dealing with mutable/ immutable objects.

理解逆变的最好方法是看到它不存在时出现的问题.尝试找出以下 Java 代码示例中的问题:

The best way to understand contravariance is to see the problem that comes when it’s absent. Try to spot the problem in the following Java code example:

Object[] arr = new int[1];
arr[0] = "Hello, there!";

您最终将字符串分配给一个整数数组.Java 通过抛出 ArrayStoreException 在运行时捕获此错误.Scala 在编译时通过强制参数类型为逆变或不变来阻止这些类型的错误.

You end up assigning the string to an integer array. Java catches this error at runtime by throwing an ArrayStoreException. Scala stops these kinds of errors at compile time by forcing parameter types to be either contravariant or invariant.

希望这会有所帮助.

这篇关于Scala 中方差和可变性/不变性之间的关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆