【问题标题】:Would an immutable keyword in Java be a good idea?Java中的不可变关键字是个好主意吗?
【发布时间】:2011-01-13 00:30:34
【问题描述】:

一般来说,我在 Java 中使用不可变对象的次数越多,我就越认为它们是个好主意。它们有很多优点,从自动成为线程安全到无需担心克隆或复制构造函数。

这让我想到,“不可变”关键字会出错吗?显然,在语言中添加另一个保留字是有缺点的,我怀疑它是否会主要出于上述原因而发生 - 但忽略我真的看不出很多缺点。

目前必须非常小心地确保对象是不可变的,即使这样,一个狡猾的 javadoc 注释声称组件对象是不可变的,而实际上它不是不可变的,这可能会破坏整个事情。还有一种观点认为,即使是像字符串这样的基本对象也不是真正不可变的,因为它们很容易受到反射攻击。

如果我们有一个 immutable 关键字,编译器肯定可以递归检查并给出一个铁定的保证,即一个类的所有实例都是不可变的,这是目前无法做到的。尤其是在并发越来越多的情况下,我个人认为在这个效果上加个关键字会更好。但是是否有任何我遗漏的缺点或实现细节使这成为一个坏主意?

【问题讨论】:

  • 这听起来没有必要,而且使用起来极其有限。
  • @Falmarri 相反,我认为不可变对象使用得相当多。
  • @Falmarri 不可变对象被大量使用——正如我所指出的,特别是随着多线程变得越来越流行,不可变对象正成为一种好的设计实践(Bloch 在有效的 Java,所以不仅仅是我这么说。)让编译器保证一个对象是不可变的,因此它的内容是线程安全的,在许多场景中确实非常有用!
  • 不可变对象是线程安全的,因为它是常量。公共静态最终类成员有什么问题?
  • @Falmarri:这里缺少两点。首先,这与线程安全无关(尽管线程安全是具有不变性的一个很好的理由)。其次,这个想法是让语言具有更容易编写正确代码的特性。说“只写正确的代码”并没有那么有用。

标签: java immutability keyword


【解决方案1】:

一般来说,不可变对象应该优先于有状态对象,it's currently fairly difficult to make an immutable object in Java。事实上,大多数 OO 语言对不可变对象的支持很差,而大多数函数式语言则认为它们是理所当然的,例如 F#、Haskell 和 Clojure。

向 Java 添加不可变关键字可以使代码...

  • 更容易编写。不要乱用 finalprivate,不要意外添加使类可变的方法,也不要手动标记类 final (subclasses can add mutable state)。
  • 更易于阅读。 English 中你不需要说类是不可变的,你可以在代码本身中说出来。不可变关键字是朝着自我记录代码迈出的良好一步。
  • 更快(理论上)。编译器对您的代码了解得越多,它可以进行的优化就越多。如果没有这个关键字,每次调用new ImmutableFoo(1, 2, 3) 都必须创建一个新对象,除非编译器可以证明你的类不能被变异。如果ImmutableFooimmutable 关键字标记,那么每次这样的调用可能返回相同的对象。我很确定new 必须总是创建一个新对象,这使得这一点无效,但与编译器的有效沟通仍然是一件好事。

Scala's case classes are similar to an immutable keyword.C# 5 中还考虑了一个不可变关键字。

【讨论】:

  • 如果你有一个不可变的类,你为什么要写setter函数?
【解决方案2】:

虽然使所有字段成为最终字段并验证任何类引用也是不可变的,但在其他情况下这变得不可能。

  • 如果你的最终类还包括一些延迟加载的字段怎么办? 人们需要进一步支持将这些字段标记为不可变和惰性。

  • 看看 java.lang.String 及其 chars[] 数组,编译器怎么能确定它是不可变的?每个人都知道字符串是,但另一个类似的类可以很容易地包含一个更新数组的方法。进一步的支持需要验证一旦设置了字段,没有其他指令可以“写入”到数组。不久之后,这将成为一个非常复杂的问题。

最后,任何这样的关键字如果确实有效,可能会有所帮助,但这并不意味着程序会更好。优秀程序员的优秀设计意味着更好的结果。即使平台限制了一些陷阱,愚蠢的程序员仍然可以写废话。

【讨论】:

  • 好点 - 我没有想到延迟加载的字段,我同意这不是一个微不足道的问题。我不认为这些障碍应该是太大的障碍。有一些方法可以绕过它们(除非我没有正确理解某些东西。)在使程序更好方面 - 我提倡它不是为了自动改进垃圾代码(它不会!)而是给优秀的编码员一个更轻松的工作。目前很容易遗漏一些小东西,从而使原本不可变的设计大开眼界;这就是我希望这样一个关键字能解决的问题。
  • 我敢肯定,如果我们坐下来仔细想想,问题的复杂性会随着更多的边缘情况而爆发,这本身就会引入更多的边缘情况。
  • 不是真正有效的点 - 缺少真正的常量数组已经是一个主要问题,因此 const 可以帮助解决字符串的问题。而且除非显式同步,延迟加载的字段已经被定义为不是线程安全的,因此无论如何都不是被标记为不可变的候选对象。
  • @SM 我的意思是这是一个复杂的问题,有很多特殊情况会蔓延,而且可能永远不会结束。不幸的是,java 还没有真正做好准备,需要有很多关键字和东西才能使它工作。您关于同步的 cmets 是这种方法需要解决的另一个问题的一个例子。
  • @Software Monkey 这并不完全正确——只要重新分配是原子的并且任何数据竞争都是良性的,具有延迟加载字段的对象仍然可以是线程安全的。 java.lang.String 的 hashCode 就是这种情况。
【解决方案3】:

我是不变性的忠实拥护者,所以在我看来,绝对如此。 OO 编程中不变性的优点数不胜数,它不应该只是函数式编程语言的领域。

【讨论】:

    【解决方案4】:

    恕我直言,面向对象的框架(Java、.net 等)应该包括更多的数组类型:可变数组、不可变数组、可变数组引用、不可变数组引用和只读数组引用(只读引用可以指向一个可变或不可变数组,但在这两种情况下都不允许写入)。如果没有不可变的数组类型,就很难以可以证明是不可变的方式构造许多有效的类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-05
      • 2015-02-03
      • 1970-01-01
      相关资源
      最近更新 更多