我创建了一个扩展 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 编译器不会抛出任何警告或错误。
为什么他们必须添加 Immutable 特征而不帮助我们防止对象可变?
最佳答案
这个问题有几个方面。
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!
由于“库”是独立于“应用程序”编译的,甚至不知道“应用程序”的存在,因此编译器无法及时捕获损坏的契约(Contract)。
2.您似乎对 trait
的作用存在更根本的误解。在这种情况下,trait
(或其他一些语言中的“interface
”)表示实现和实现之间的契约关于实现如何以及应该如何表现的用户代码。然而,并不是每种合约都可以表示为特征(至少不会使代码变得 super 复杂)。例如,对于可变集合,有一个约定,size
应返回调用 add
(或 +=
)的次数,但是除了声明具有相应签名的方法 size
和 +=
之外,无法将此类合约表示为 trait
。另一方面,对于大多数契约(Contract)来说,没有办法强制执行以遵循契约(Contract)。例如,总是返回 0
的 size
实现在技术上匹配所有类型,但显然违反了约定。
类似地,不可变
文档说:
A marker trait for all immutable data structures such as immutable collections.
所以它只是一个标记trait
,它是解决无法真正表示为类型的契约的方法之一。它表示,无论谁实现了该特征,都声明是一个不可变的对象。您的代码声称但显然违反了契约(Contract)。因此从技术上讲,不遵守契约(Contract)是你的错。
关于scala - 为什么我可以更新 scala 中扩展不可变特征的对象的状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54139628/