我刚刚参加了暑期学校的Scala讲座。讲师得到了以下问题:
- “编译器有什么办法可以判断一个类是不可变的吗?”
讲师回应了
- “不,没有。如果可能的话会非常好。”
我很惊讶。 是否仅仅检查该类是否包含任何var-members?
答案 0 :(得分:7)
什么是不可变的?
检查对象是否仅包含val
字段是不可变性的过度扩展 - 对象可能很好地包含var
s,但从不在其中分配不同的值。或者,为var
s分配值的程序段可能无法访问。
根据Chris Okasaki的术语,有不可变的数据结构和功能数据结构。
不可变数据结构(或类)是一种数据结构,一旦在内存中构造,就永远不会更改其组件和值 - 这个例子就是Scala元组。
但是,如果您将对象的不变性定义为自身的不变性以及通过对象引用可以访问的所有对象,那么元组可能不是不可变的 - 它取决于您稍后使用它实例化的内容。有时在编译时没有足够的关于程序的信息来确定给定的数据结构在仅包含val
的意义上是否是不可变的。并且由于多态性而缺少信息,无论是参数化,子类型还是ad-hoc(类型类)。
这是决定不变性的第一个问题 - 缺乏静态信息。
功能数据结构是一种数据结构,您可以在该数据结构上执行其输出仅取决于给定状态的输入的操作。这种数据结构的一个例子是搜索树,它通过将最后一个项目存储在可变字段中来缓存该项目。即使每个查找都会将搜索到的最后一项写入可变字段,因此如果再次查找该项,则不必重复搜索,这样的数据结构的查找操作的输出始终保持不变没人在里面插入新物品。功能数据结构的另一个例子是splay trees。
在一般命令式编程模型中,要检查操作是否纯粹,即 - 输出仅依赖于输入,是undecidable。同样,人们可以使用抽象解释等技术来提供保守的答案,但这并不是纯粹问题的准确答案。
这是决定具有var
s的东西是不可变的还是功能性的(显然是不可变的)的第二个问题 - 不可判定性。
答案 1 :(得分:5)
我认为问题在于您需要确保所有val
的成员都没有var
成员。这你不能。考虑
class Base
case class Immutable extends Base { val immutable: Int = 0 }
case class Mutable extends Base { var mutable: Int = _ }
case class Immutable_?(b: Base)
即使Immutable_?(Immutable)
确实是不可变的,Immutable_?(Mutable)
也不是。
答案 2 :(得分:2)
如果在val中保存可变对象,则对象本身仍然是可变的。因此,您必须检查在val中使用的每个类是否都是不可变的。
case class Mut(var mut:Int)
val m = Mut(1)
println(m.toString)
m.mut = 3
println(m.toString)
答案 3 :(得分:2)
除了其他人所说的内容外,请查看effect systems和discussion about supporting one in Scala。
答案 4 :(得分:1)
这不是那么容易,因为你可能有vals链接到其他可变类,或者甚至更难检测,调用其他类中的方法或可变的对象。
另外,你很可能有一个实际上有变量的不可变类(例如......更高效)。
我想你可能会有一些东西可以检查一个类看起来是否它是不可变的,但是听起来它可能非常令人困惑。
答案 5 :(得分:0)
您可以拥有一个可以实例化为对象的类,并且此对象可以是可变的或不可变的。
示例:类可能包含List[_]
,在运行时可以是List[Int]
或List[StringBuffer]
。因此,类的两个不同对象可以是可变的,也可以是不可变的。