首页 > 解决方案 > 为什么我可以在scala中更新对象扩展不可变特征的状态

问题描述

我创建了一个类扩展 scala.Immutable

  class SomeThing(var string: String) extends Immutable {
      override def toString: String = string
  }

正如我所料,scala 编译器应该帮助我防止更改类 SomeThing 的状态。但是当我运行这个测试时

  "Test change state of immutable interface" should "not allow" in {
     val someThing = new SomeThing("hello")
     someThing.string = "hello 1"
     println(someThing)
  }

结果是hello 1scala 编译器不会抛出任何警告或错误。

为什么他们必须添加不可变特征而不帮助我们防止对象可变?

标签: scala

解决方案


这个问题有几个方面。

1.一个简单的原因是 Scala 编译器无法真正确保不变性,原因有很多。例如,主要目标平台 JVM 允许final使用反射修改偶数字段。这不可执行的另一个原因是这样的代码

 /////////////////////////////////////////
 //// library v1
 package library

 class LibraryData(val value:Int)


 /////////////////////////////////////////
 //// code that uses the library
 package app

 class UserData(val data:LibraryData) extends Immutable



 /////////////////////////////////////////
 //// library v2
 package library

 class LibraryData(var value:Int) //now change it to var!

由于“库”是独立于“应用程序”编译的,甚至不知道“应用程序”的存在,因此编译器没有时间点可以捕捉到损坏的合同。

2.你似乎有更根本的误解是什么trait。在这种情况下trait(或interface某些其他语言中的“ ”)表示实现和用户代码之间关于实现如何以及应该如何表现的契约。然而,并非每种合约都可以表示为trait(至少不会使代码变得超级复杂)。例如,对于可变集合,有一个合约应该返回被调用size的次数add(或),但是除了声明有方法和+=traitsize+=带有相应的签名。另一方面,对于大多数合同来说,没有办法强制执行以遵循合同。例如,size始终返回0技术上匹配所有类型但显然违反合同的实现。

同样Immutable的文档说:

所有不可变数据结构(例如不可变集合)的标记特征。

所以它只是一个标记trait,它是解决不能真正表示为类型的合同的一种方法。它说实现该特征的任何人都声称自己是不可变对象。您的代码声称但显然违反了合同。所以从技术上讲,不遵守合同是你的错。


推荐阅读