是否有可能在Java中使用不可变的最终类?

时间:2016-07-10 21:20:32

标签: java immutability

最近在采访中我提出了一个有趣的问题。 我们有可变类:

if let loggedIn = result?["loggedIn"] as? String where loggedIn == "1" { ...

此类的实例final class Example { private int i; private String s; private Object o; // get, set }

我们可以以某种方式使这个实例不可变吗?不改变原班级。

我的想法:

  1. 深度克隆此实例?但不确定是否可能。
  2. 可能类似于序列化/反序列化?

3 个答案:

答案 0 :(得分:2)

如果您无法修改Example,则无法对其进行子类化(在您的代码段中,标记为final),我可以使用最接近的解决方案想到的是创建一个包装类,它是不可变的。这不是一个完美的解决方案,并且有它的缺点。

首先,如何做到:

final class ImmutableExample {
  // Redeclare every field as in the Example class
  // but make sure they can't be reassigned 
  // (in this case I'll declare them as final)
  private final int i;
  private final String s;
  private final Object o;

  ImmutableExample(Example mutableExample) {
      // copy fields from original
      this.i = mutableExample.getI();
      this.s = mutableExample.getS();
      this.o = mutableExample.getO();
  }

  // add getters but definitely no setters
}

然后到处都有这样的代码:

Example e = new Example();
e.setI(42); // etc

更改为:

Example e = new Example();
e.setI(42); // etc
ImmutableExample immutableE = new ImmutableExample(e);

并传递对immutableE的引用,并确保e引用不会转义。

现在,出于缺点:

  • ImmutableExample不是Example的实例,因此您无法将不可变类型传递给期望可变类型的方法,以及if (immutableE instanceof Example)或{{1}等操作不会像以前一样工作

  • 您必须非常小心,(Example)immutableE的每个字段也是不可变的,或Example也是可变的。例如,请考虑ImmutableExample类型的字段可能是可变的,例如ObjectHashMap

  • Date班级发生变化时,您必须重复Example中的更改。

如果可以继承ImmutableExample,或者它是一个接口,这种方法可能更有用,但是当Example无法被子类化时,我无法看到任何其他方式。

答案 1 :(得分:0)

如果每个字段都有getter / setter,那么为了使它不可变,你将不得不

  1. 制作每个字段privatefinal
  2. 在调用getter的时候制作每个字段的副本
  3. 删除所有setter
  4. 必须删除类中更改其状态的任何方法,或使用新的getter访问任何内部

答案 2 :(得分:0)

不变性是一个类的属性,而不是一个实例。所以除了字节码twiddling或其他方式改变类;不可能。

如果没有最终类,我会创建一个不可变的装饰器。这不会使实例成为不可变的,而是为该实例提供一个不可变的包装器。

您无法将实例分配给任何变量/字段,因此无法更改它;)