Scala - 可变集合中的协变类型

时间:2018-03-21 15:52:18

标签: scala immutability covariance mutability invariance

我是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)实际上有哪种everythingStringAny?声明是“val everything: ListBuffer[Any]”,因此我希望Any因为所有内容都应该是Any的类型,所以我认为Integer和{{{{}}}没有任何问题1}}在一个String中。如何将整数值存储到字符串集合中如何写?为何灾难???我为什么要使用List(不可变)而不是ListBuffer(可变)?我没看见有分别。我找到了很多答案,可变集合应该具有类型不变量,并且不可变集合应该具有协变类型,但为什么呢?

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

非常感谢,

安德鲁

2 个答案:

答案 0 :(得分:5)

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

  1. "实际上" everythingListBuffer[_],已删除参数类型。根据JVM,它对某些对象保持32位或64位引用。类型ListBuffer[String]ListBuffer[Any]是编译器在编译时知道的类型。如果它"知道"两件相互矛盾的事情,那显然非常糟糕。
  2. "我没有看到任何问题让Integer和String in 一个ListBuffer [Any]" 。在IntStringListBuffer[Any]没有问题,因为ListBuffer是不变的。但是,在您的假设示例中,ListBuffer是协变的,因此您要在Int中存储ListBuffer[String]。如果有人稍后从Int获得ListBuffer[String],并尝试将其解释为String,那么它显然非常糟糕。

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

  4. "为什么灾​​难???" 它不会是一场重大灾难。 Java一直与协变阵列一起生活。它不会导致灾难,它只是坏事和烦人。

  5. "我为什么要使用List(这是不可变的)而不是ListBuffer(这是可变的)?" 没有绝对的命令可以告诉你永远使用List并永远不要使用ListBuffer。在适当的时候使用两者。在99.999%的情况下,List当然更合适,因为您使用List来表示数据的方式比设计需要ListBuffer的本地可变状态的复杂算法更频繁。

  6. "我找到了许多可变收集的答案 应该具有类型不变量和不可变集合应该 有协变类型,但为什么?" 。这是错误的,你过度简化了。例如,内涵不可变集既不应该是协变的,也不应该是不变的,而是逆变。在适当的时候,你应该使用协方差,逆变和不变性。 Drive Tables,也许你觉得它很有用。

  7. 2 这是This little silly illustration has proven unreasonably effective for explaining the difference,就像下面的例子一样:

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

    这是singleton type

答案 1 :(得分:0)

我同意@Andrey所说的大部分内容我只想补充一点,协方差和逆变只属于不可变结构,书中提出的练习只是一个例子,所以人们可以理解但是不可能实现一个可变的具有协变性的结构,您无法使其编译。 作为练习,您可以尝试实现<div class="modal fade" id="edit" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <h4 class="modal-title" id="myModalLabel">Editer mon Profile</h4> </div> <div class="modal-body"> <div class="span4 offset2 cards"> {{ form_start(form, { 'action': path('fos_user_profile_edit'), 'attr': { 'class': 'fos_user_profile_edit' } }) }} {{ form_widget(form) }} <div> <input class="btn btn-success" type="submit" value="{{ 'profile.edit.submit'|trans }}" /> </div> {{ form_end(form) }} </div> </div> </div><!-- /.modal-content --> </div><!-- /.modal-dialog --> </div><!-- /.modal --> ,但是如果不欺骗编译器将<script src="{{ asset('js/bootstrap.js') }}"></script> <script src="{{ asset('js/modal.jss') }}"></script> <link href="{{ asset('css/bootstrap.css') }}" rel="stylesheet"> 放在任何地方

,您将发现无法做到这一点