java中的不可变类

时间:2014-11-01 10:26:02

标签: java immutability

是否需要将Immutable类private的字段标记为final,无法更改?
我的意思是仅将字段标记为final是不够的?

(我知道不可变类没有必要有最终字段,但建议在内部编译时检查该类。)

3 个答案:

答案 0 :(得分:7)

这还不够。

考虑这个例子:

final class ImmutableClass {
    private final List<String> data;

    public ImmutableClass(final List<String> data) {
        this.data = data;
    }

    public List<String> getData() {
        return data;
    }
}

此类为final,因此无法扩展。它的data字段为final,因此在分配后无法更改。

<强>但是

final ImmutableClass immutableClass = new ImmutableClass(data);
immutableClass.getData().add("Some other value");

糟糕。

因此,要使一个类真正不可变,所有字段也应该是不可变类,或具有防御性副本

例如,要纠正相关问题,您需要执行此操作:

final class ImmutableClass {
    private final List<String> data;

    public ImmutableClass(final List<String> data) {
        this.data = new ArrayList<>(data);
    }

    public List<String> getData() {
        return Collections.unmodifiableList(data);
    }
}

这只有效,因为 String是不可变的。例如,如果您有List<Date>,则还需要复制单个Date个对象。

答案 1 :(得分:1)

final字段只能从构造函数中分配。因此,在构造之后,使字段final足以使它们不可变。为可变对象分配指针是另一回事。

答案 2 :(得分:1)

通过getter访问私有变量可以让您更自由地实现类。例如,您可以使用实现来替换一个简单的getter,该实现计算或检索访问时的值或缓存结果。

即使字段是最终字段,将字段保持为私有的另一个原因是字段是可变对象。在这种情况下,对象可以更改,即使它是最终的,通常不是你想要的。这是一个可能是不可变类的示例,其中包含可变Date类的公共final字段。

 class DateHolder {
      public final Date date;
      DateHolder(Date date) {
           this.date = date:
      }
 }

 // ...

 DateHolder holder = new DateHolder(Date.now());

 // this doesn't work because date is final:
 //holder.date = new Date(2013, 11, 23);

 // but this works even though date is final:
 holder.date.setYear(2013);