【问题标题】:How do we make a java class immutable if it has a member variable of another user defined class?如果 java 类有另一个用户定义类的成员变量,我们如何使它不可变?
【发布时间】:2016-03-04 12:00:00
【问题描述】:

最近在 Java 开发人员角色的面试中,有人问我如何使 Class A 不可变,如果它有一个成员变量,它是 Class B 的对象,并且在 Class B 是项目外部并且不能由程序员编辑,而且类 B 甚至可能有一个自己的成员变量,它是另一个用户定义类的对象。我想了很多,并告诉面试官,除非 B 类已经实现并暴露了一个方法来深度克隆自己,否则没有办法。

但面试官并不相信。真的有办法让这样的类不可变吗?

如果我没记错的话,这就是他解释的情况。他想让我让 A 类不可变,最好的答案是什么?

final public class A {
    final private B b;

    A(B b) {
        this.b = b; // Class b might/might not be cloneable
        // this.b = (B)b.clone();
    }

    public B getB() {
        return b;
        // return (B)b.clone();
    }
}

class B // external cannot edit
{
    C c;

    public C getC() {
        return c;
    }

    public void setC(C c) {
        this.c = c;
    }
}

class C // external cannot edit
{
    int i;
    String j;

    public int getI() {
        return i;
    }

    public void setI(int i) {
        this.i = i;
    }

    public String getJ() {
        return j;
    }

    public void setJ(String j) {
        this.j = j;
    }
}

【问题讨论】:

  • 不要为它写getter,写委托给不修改B状态的方法?
  • 你必须在你设置或获取它的每个地方自己进行深度复制。

标签: java immutability deep-copy defensive-copy


【解决方案1】:

不要将 B 暴露给世界。所以没有返回 B 的方法。

而是识别 B 中的方法,这些方法不会改变 B 并让 A 通过调用 b 中的相同方法来实现这些方法。

所以如果 B 有一个方法 calcSomething() a 应该有一个 calcSomething() 方法,它只返回 b.calcSomething()。

【讨论】:

  • 这本来是我的答案,但它不是 100%,提问者是正确的,这实际上是不可能的。无主包中的无主类可以引用一个无主静态变量,该变量可以在外部发生变异。我想您可以更进一步,将外部类的状态复制到本地成员变量中——然后您将拥有一个良好的不可变状态,但无法从潜在可变类访问方法代码。
【解决方案2】:

你可以这样使用:

final public class A {
    final private B b;

    A(B b) {
        this.b = cloneB(b); 
    }

    public B getB() {
        return cloneB(b);
    }

    private static B cloneB(b){ 
        B newB = new B(); 
        C c = new C();
        c.setI(b.getC().getI());
        c.setJ(b.getC().getJ());
        newB.setC(c); 
        return newB;
    }
}

如果 A 类是 100% 不可变的。

更新:您也可以使用反射或序列化来获取类的深层副本(如果类具有较深的层次结构),例如使用 GSON 进行序列化:

private static B cloneB(b){ 
    String tmp = new GSON().toJson(b);
    return new GSON().fromJson(tmp, B.class);
}

等等

【讨论】:

  • 实际上,当我给 C 类的答复是我不会向 B 提供 getter 而是在 A 类本身中将委托写入不暴露其状态的 B 的方法时,C 类就出现了。用户定义类的成员变量的深度可以是任何东西,只是为了在这里解释它的 3。 C 可能有 D 等等。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多