2

我是 Scala 世界的新手,现在我正在阅读名为“Scala in Action”的书(Nilanjan Raychaudhuri 着),即第 97 页上名为“可变对象需要保持不变”的部分,我不明白以下部分直接取自上述书中。


假设 ListBuffer 是协变的,并且以下代码片段可以正常工作,没有任何编译问题:

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 类型,所以您可以将整数值存储到字符串集合中。这是一场等待发生的灾难。为了避免这类问题,让可变对象保持不变总是一个好主意。


我会有以下问题..

1)everything现实中是什么类型?String还是Any?声明是“ val everything: ListBuffer[Any]”,因此我会期望Any,因为一切都应该是类型,Any那么我看不出有Integer任何String问题ListBuffer[Any]。如何将整数值存储到字符串集合中,它们是如何编写的???何为灾难???为什么我应该使用 List(它是不可变的)而不是 ListBuffer(它是可变的)?我看不出有什么区别。我找到了很多答案,可变集合应该具有类型不变,不可变集合应该具有协变类型,但为什么呢?

2)最后一部分“ res4: everything.type = ListBuffer(1, pants)”是什么意思?“everything.type”是什么意思?我猜它everything没有任何方法/函数或变量称为type.. 为什么没有 ListBuffer[Any] 或 ListBuffer[String]?

非常感谢,

安德鲁

4

2 回答 2

5

1这看起来不像一个问题,所以我必须进一步细分:

  1. “实际上”everythingListBuffer[_],具有已擦除的参数类型。根据 JVM,它保存对某些对象的 32 位或 64 位引用。类型ListBuffer[String]ListBuffer[Any]是编译器在编译时所知道的。如果它“知道”两件相互矛盾的事情,那它显然是很糟糕的。
  2. “我认为在一个 ListBuffer[Any] 中包含 Integer 和 String 没有任何问题”。有 和 没有问题IntString因为ListBuffer[Any]ListBuffer不变的。但是,在您的假设示例中,ListBuffer是协变的,因此您将 a 存储Int在 a 中ListBuffer[String]。如果后来有人从 a 中得到Inta ListBuffer[String],并试图将其解释为String,那么这显然是非常糟糕的。

  3. “如何将整数值存储到字符串集合中?” 如上所述,您为什么要做一些明显非常糟糕的事情?

  4. “为什么是灾难???” 这不会是一场大灾难。Java 一直使用协变数组。它不会导致灾难,只是很糟糕而且很烦人。

  5. “为什么我应该使用 List(它是不可变的)而不是 ListBuffer(它是可变的)?” 没有绝对的命令告诉您始终使用List和从不使用ListBuffer. 在适当的时候使用两者。在 99.999% 的情况下,List当然更合适,因为您使用Lists 表示数据的方式比您设计需要 a 的局部可变状态的复杂算法更频繁ListBuffer

  6. “我找到了很多答案,可变集合应该具有类型不变,不可变集合应该具有协变类型,但为什么呢?” . 这是错误的,您过于简单化了。例如,内涵不可变集既不应该是协变的,也不应该是不变的,而是逆变的。您应该在适当的时候使用协变、逆变和不变性。这个愚蠢的小插图已被证明对解释差异非常有效,也许你也觉得它有用。

2这是一个单例类型,就像下面的例子一样:

scala> val x = "hello"
x: String = hello

scala> val y: x.type = x
y: x.type = hello

这是关于这样做的动机的更长讨论

于 2018-03-21T16:17:28.770 回答
0

我同意@Andrey 所说的大部分内容,我只想补充一点,协变和逆变完全属于不可变结构,书中提出的练习只是一个示例,因此人们可以理解,但不可能实现可变结构协变,你将无法编译它。作为一个练习,你可以尝试实现 a MutableList[+A],你会发现没有办法做到这一点而不欺骗编译器asInstanceOf到处放

于 2018-03-21T16:50:38.153 回答