为什么我可以在Scala中更新对象的状态扩展不可变特征

时间:2019-01-11 02:33:52

标签: 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 1,并且scala编译器不会引发任何警告或错误。

为什么他们必须在不帮助我们防止对象可变的情况下添加不可变特征?

1 个答案:

答案 0 :(得分:3)

这个问题有几个方面。

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(或+=)的次数,但无法将这样的合同表示为: trait除了声明存在具有相应签名的方法size+=之外。另一方面,对于大多数合同,没有办法强制执行以遵循合同。例如,从技术上总是返回size的{​​{1}}的实现在技术上匹配所有类型,但显然违反了合同。

类似地,0文档说:

  

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

因此,它只是标记Immutable,它是解决无法真正表示为类型的合同的方法之一。它说,凡实施该特征的人 宣称 是一个不变的对象。您的代码声称,但显然违反了合同。因此,从技术上讲,您不遵守合同是您的错。