java - 如何确定一个类是否不可变

标签 java oop immutability

在具体说明我的问题之前,让我提供一些背景知识:我的主要编程语言是 C++ 和 Java。在使用 C++ 时,我发现应用 const 正确性很重要,即声明这样的函数:

A::DoSomething( const B& arg ); // guarantees that arg is not modified
A::DoSomething() const; // guarantees that the object of type A is not modified
A::DoSomething( const B& arg ) const; // both of the above

事实上,我经常希望 const 是默认值,修改的对象必须以某种方式标记。

我使用 const 的主要原因是:

  • 与其他开发人员的交流:它使代码更具表现力。
  • 与编译器沟通:它有助于在编译时发现问题,有时还可以进行额外的优化。

众所周知,Java 没有 const 关键字(你不能用 final 做上面的事情),这个事实已经在这里讨论过,参见此处示例:Equivalent of const(C++) in Java .

通常提出的 Java 替代方案是使您的类不可变。虽然这不是 const 的完全替代,因为它适用于每个类而不是每个使用类的上下文,但在大多数情况下它对我来说效果很好。

但是不可变类有一个大问题:不可变性并不明显。要确定一个类是否真的不可变,据我所知,您基本上必须检查完整的源代码。任何方法都可能有一个后门,通过该后门可以修改对象。

那么有没有更简单的方法来检查不变性?或者是否有任何最佳实践以某种方式将类标记为不可变?

注意:我知道这两种语言都提供了绕过常量性或不变性的“邪恶技巧”:C++ 有 const_cast 而 Java 有反射。但是对于我的问题的上下文,我们假设这些没有被使用。

最佳答案

Java 没有一流的不可变性支持,因此您没有可靠的方法来检测类是否不可变。

Java Concurrency In Practice建议(参见附录 A 作为引用)使用来自 javax.annotation.concurrent 的类级 @Immutable 注释,这是指示不变性的最方便、常见和标准的方法;自定义 javadoc 也很好。请注意,这只是声明,而不是实际约束。

类的设计也是一个很好的指标:

  • 只有 final 字段(但在极少数情况下,可能有一些非 final 字段并且仍然是不可变的,例如参见 String#hashCode)
  • 构造正确(this 引用不会从构造函数中泄漏)
  • 对象状态不能被修改(所以类不应该有 setter 和 mutator 方法)
  • 不要存储对可变对象的外部(传递给构造函数)引用(例如,创建传递的集合参数的防御副本)

不可变类设计属性的完整列表可以在 Oracle tutorials 中找到.

因此,要检查类是否不可变,您首先要查看类级别的注释和 javadoc,然后再查看实现本身。

为了提供额外的完整性检查(如果您认为注释为不可变的类可能会错误地可变),有 Mutability Detector FindBugs(静态分析工具)的插件,它有效地做上面列出的同样的事情:检查类有 @Immutable 注释并验证(通过反射)所有不变性规则是否满足(有一些额外的东西,比如不可变集合Guava 等的支持)。可变检测器也可以用作没有 FindBugs 的库,它允许您编写这样的测试:

@Test
public void testImmutable() {
    assertImmutable(MyClass.class);
}

关于java - 如何确定一个类是否不可变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37087809/

相关文章:

java - 条件和编辑文本

java - 为什么在 Weighted QuickUnion 中联合和查找操作期间的数组访问次数据说是 lg(N) 的顺序?

c++ - 派生类销毁期间纯虚函数的范围 - 在 C++ 中

concurrency - 为什么 ReadOnlyDictionary 不是线程安全的?

java - 如何在命令行模式下运行时向 JMeter csv 结果文件添加日期

java - 在 Java 上制作数据结构的最惯用方法

php - 什么时候从程序切换到 OOP?

regex - 为什么 perl6 regex ~~ 试图分配给不可变容器?

javascript - 从嵌套对象中删除数据而不改变

java - 使用 JAXB 解码 XML 而无需转义字符