Как сделать класс java неизменным, если в нем есть переменная-член другого пользовательского класса?

Недавно в интервью для роли Java Developer меня спросили, как сделать Class A неизменным, если у него есть переменная-член, которая является объектом Class B, и в ситуации, когда Class B является внешним по отношению к проекту и не может быть отредактирован кроме того, у программиста класса B может быть даже собственная переменная-член, которая является объектом другого определенного пользователем класса. Я много думал об этом и сказал интервьюеру, что нет никакого способа, если класс В не внедрил метод и не подверг его глубокому клонированию.

Интервьюер, хотя и не был убежден. Есть ли способ сделать такой класс неизменным?

Если я правильно помню, это была ситуация, которую он объяснил. Он хотел, чтобы я сделал класс А неизменным, что было бы лучшим ответом?

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;
    }
}

2 ответа

Не выставляй Б миру. Так что нет метода, который возвращает B.

Вместо этого определите методы в B, которые не видоизменяют B и позволяют A реализовать эти методы, вызывая тот же метод в b.

Поэтому, если у B есть метод calcSomething(), a должен иметь метод calcSomething(), который просто возвращает b.calcSomething().

Вы можете использовать что-то вроде этого:

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);
}

или так далее

Другие вопросы по тегам