【问题标题】:Ensure that class is immutable确保类是不可变的
【发布时间】:2015-01-08 16:19:58
【问题描述】:

在this 6 岁问题中,最佳答案说无法验证不变性。另一方面,在底部有两个最近的答案表明可以使用:

检测类是否不可变。

我的问题是:这些工具在现实生活中有用吗?还有其他(更好的)解决方案吗?这几年有什么变化吗?

【问题讨论】:

  • 我更担心在过去 6 年中最先进的技术是否发生了变化。我知道possible duplicate 线程。
  • 遗憾的是,没有一种最流行的语言(实际上我所知道的没有一种语言)本身就支持不变性。那会解决很多问题。
  • 随着我们似乎达到时钟速度的极限并且我们正在转向越来越多的内核,多线程变得越来越重要。不幸的是,共享资源的多线程是一门高级艺术,只有专业程序员才能实践,因为它本质上只是“等待发生的错误”。另一种方法是不共享多线程,它严重依赖于不变性,因此我们确实需要工具来保证和执行它。想想 n00bs!

标签: java immutability


【解决方案1】:

虽然我确信有很多工具可以直接测试一个类是否不可变,但真正的问题是测试允许具有接口或抽象类声明的字段的不可变类。基本上只能允许密封的不可变类作为子字段,因为继承通常会破坏不变性。

如果不使用 Java 集合 API(即不使用 Map、List 或 Set 等),很难制作复合不可变对象。

另一个问题是,仅仅因为一个字段是final 并不能真正使它成为“最终”。许多像 Hibernate 这样的序列化工具会很高兴地构造不可变对象并通过反射设置最终字段。

我看到的唯一真正的方法是检测真正的不变性是通过运行时分析,但您的代码可能需要很长时间才能真正触及发生可变性的特定用例,我认为这种检测会相当昂贵.

不可变对象的另一种有趣的假设推论是,它们在构造时不应该改变外部的东西。那就是构造一个不可变对象应该没有副作用。这是工具将遇到困难的地方。您可以使用构造函数轻松创建不可变对象,该构造函数执行各种外部操作,例如启动线程或设置系统属性。

【讨论】:

  • 你可以改用番石榴Immutable Collections。
  • 是的,但这些是具体的类。基本上,您永远不能在不可变对象中允许接口。
  • @Tom ImmutableList 也不是最终的,因此您可以在其构造函数中轻松地构造一个需要ImmutableList 的对象,并带有一个顽皮的ImmutableList。也很好奇......你认为支持ImmutableList.. 我猜是一个非常可变的数组。据我们所知,一个糟糕的ImmutableList 可以在其构造函数中创建一个线程并随机更改支持数组。
  • 我都同意。没有想过邪恶的子类。
  • 另外,正如您正确指出的那样,有时有趣的不仅是不变性,而且是“无副作用”。我非常不尝试解决这个问题,因为它很难(tm)。特别是在 JVM 上。总而言之,我相信 Mutability Detector 是有用的,即使它不能“证明”任何事情,因为它可以提供更大的信心,例如有人无意中引入了 setter 方法。很抱歉有很多回复,希望它们是有用的,而不是垃圾邮件:)
【解决方案2】:

关于一个工具在现实生活中是否有用,这取决于你为什么需要不可变性(正如引用的帖子所问的那样,你是在运行时验证,还是通过检查,或者防止恶意代码,或者只是确保你自己的代码是不可变的?)。如果您手动检查一个类是不可变的,最简单的保证是所有字段写入仅发生在构造函数中,或者所有字段都是最终的(或两者兼而有之)。

但这仍然是有限的,因为“最终对象 x”确保您的引用不会改变,但不能保证被引用的对象不会改变。正如亚当所逃避的那样,如果那是一个列表,则基础列表可能会发生变化,这可能会导致您的(否则不可变的)类中出现线程问题。如果您使用列表中的值来确定您的哈希码(您使用 x.hashCode() 作为生成自己的哈希码的一部分),那么您的哈希码将违反其他规则(因此列表的不可变视图不是) t 甚至足够)。

如果您的不可变类不会受到对持有对象的更改的影响,那么它可能适合您的目的。

运行时检查必须受到时间/深度/范围的限制,因此不可能完美。这些工具(或任何类似的运行时方法)有一些用途,但这实际上取决于您需要确定不变性的原因。如果您可以将不变性指定为 API 要求的一部分,检测违反该规则的情况(例如对象更改的 hashCode),并在它们更改时抛出异常,那么您不需要自己检测它。或者相信你的来电者。如果您正在尝试编写不可变类,那么这些工具根本没有帮助。如果您真的在寻找线程安全性,那么不变性本身就是一个不充分的答案。

对于这个问题,这不是一个令人满意的答案(这是一个很好的答案),但我认为在大多数情况下,如果你更仔细地研究这个问题,最终会是一个错误的问题。

【讨论】:

  • 对 Mutability Detector 的实现做了几个 cmet,任何阅读的人都可能想参考。您对 final Object x 字段发生突变的担忧是完全正确的。这就是为什么 Mutability Detector 的分析会在任何字段不是不可变的情况下发出警告,并提出警告。这当然是可传递的,Object 类型在运行时可以是任何东西,因此它使类可变,但如果该字段是 Foo 类型,其中Foo 是最终的且不可变的,它不会引起警告分配为字段时。
  • 我完全同意“如果你真的在寻找线程安全,那么不变性本身就是一个不充分的答案。”但我不同意“如果您尝试编写不可变类,那么这些工具根本没有帮助。”。 Mutability Detector 的分析并不可靠,因此它不能保证正确性,但它可以告诉您是否明显搞砸了某些事情,例如不小心引入了setter方法。该分析可以在单元测试中自动重复运行,以廉价地发现错误。如果您的目标是引入不可变对象以实现线程安全,那确实有帮助。
  • 对不起,我应该说“那可以帮助”,但当然是 YMMV。我发现它很有帮助,但作为作者,我显然有偏见:)
  • 公平:是的,即使使用自己的代码也有帮助(我的意图不是降低上述工具的价值)。如果我正在编写课程,我可能会采用其他方法(例如 finality)。我的倾向是,当我无法访问代码时,最好使用此类工具(如果我可以访问代码并且可以对其进行修改,那么可以使用更大的选项)。
  • 有趣,如果您可以访问代码,您会使用哪些工具?我使用 Mutability Detector 的目的之一是允许在开发时对您自己的代码进行单元测试以确保其不变性。另一个用例是对其他人的代码进行运行时分析,但执行的是相同的静态分析。
猜你喜欢
  • 2012-11-19
  • 1970-01-01
  • 1970-01-01
  • 2010-11-20
  • 2017-09-17
  • 1970-01-01
  • 2014-02-24
  • 2011-05-20
  • 1970-01-01
相关资源
最近更新 更多