制作不可变的Java对象

Ruc*_*era 63 java immutability

我的目标是使Java对象不可变.我上课了Student.我用以下方式对其进行编码以实现不变性:

public final class Student {

private String name;
private String age;

public Student(String name, String age) {
    this.name = name;
    this.age = age;
}

public String getName() {
    return name;
}

public String getAge() {
    return age;
}

}
Run Code Online (Sandbox Code Playgroud)

我的问题是,实现Student课堂不变性的最佳方法是什么?

ass*_*ias 66

严格来说,你的课不是一成不变的,它只是有效的一成不变的.要使其不可变,您需要使用final:

private final String name;
private final String age;
Run Code Online (Sandbox Code Playgroud)

虽然差异可能看起来微妙,但它可以在多线程上下文中产生显着差异.不可变类本质上是线程安全的,只有在安全发布时,有效不可变类才是线程安全的.


Roh*_*ain 56

创建不可变类时必须考虑的事情很少:

  • 上课final- 你已经有了
  • 创建所有字段privatefinal- 在代码中进行适当的更改
  • 不要提供任何更改实例状态的方法
  • 如果您的班级中有可变字段List,或者说Date,使它们final不够用.你应该从他们那里返回一个防御性副本getters,这样他们的状态就不会通过调用方法来改变.

对于第4点,假设你Date的班级中有一个字段,那么该字段的getter应如下所示:

public Date getDate() {
    return new Date(this.date.getTime());
}
Run Code Online (Sandbox Code Playgroud)

当你的可变字段本身包含一些可变字段,并且反过来可以包含一些其他可变字段时,制作防御性副本会变得很头疼.在这种情况下,您需要迭代地复制每个副本.我们将此可变字段的迭代副本命名为Deep Copy.

自己实施深层复制可能很麻烦.但是,要将这个问题区分开来,一旦你发现自己陷入了制作深度防御性副本的要求,就应该再次考虑你的课堂设计.

  • @arshajii - 如果对象的内部状态可以更改,则该对象不是不可变的.如果内部状态进入`equals`或`hashCode`的逻辑,则内部状态(间接)对外界可见. (3认同)

Pre*_*raj 6

如何使可变对象不可变?

  1. 将类声明为 final 使其无法扩展。
  2. 将所有字段设为私有,以便不允许直接访问。
  3. 不要为变量提供 setter 方法
  4. 将所有可变字段设为 final,以便其值只能分配一次。
  5. 通过执行深复制的构造函数初始化所有字段。
  6. 在 getter 方法中执行对象克隆以返回副本而不是返回实际的对象引用。

来源

我们为什么要创建不可变对象?
不可变对象只是其状态(对象的数据)在构造后无法更改的对象。

  • 易于构建、测试和使用
  • 自动线程安全并且没有同步问题
  • 不需要复制构造函数
  • 不需要克隆的实现
  • 允许 hashCode 使用延迟初始化,并缓存其返回值
  • 用作字段时不需要防御性复制
  • 制作好的 Map 键和 Set 元素(这些对象在集合中不能改变状态)
  • 在构造时建立它们的类不变量,并且永远不需要再次检查
  • 始终具有“失败原子性”(Joshua Bloch 使用的术语):如果不可变对象抛出异常,则它永远不会处于不良或不确定状态

来源