我是否总是需要克隆对象以确保封装在 Java 中?

Do I always need to clone an object to ensure encapsulation in Java?

给定一个 Outer class,它引用 Inner class 的一个对象作为参数:

public class Outer {

    private Inner inner;

    public Outer(Inner inner) {
        // fails
        // this.inner = inner;

        // passes
        this.inner = this.clone(inner);
    }

    public Inner getInner() {
        return this.inner;
    }

    private Inner clone(Inner inner) {
        return new Inner(inner.getInnerValue());
    }
}

和只有整数值的Innerclass

public class Inner {

    private int innerValue;

    public Inner(int innerValue) { this.innerValue = innerValue; }

    public void setInnerValue(int innerValue) {
        this.innerValue = innerValue;
    }

    public int getInnerValue() {
        return this.innerValue;
    }
}

测试

class OuterTest {
    @Test
    void testEncapsulation() {
        Inner inner = new Inner(3);
        Outer outer = new Outer(inner);
        inner.setInnerValue(4);
        assertEquals(3, outer.getInner().getInnerValue());
    }
}

只有在我克隆 inner 时才会通过(参见评论 fails)。大体上是这样吗?那么我是否需要在每次通过引用时都克隆每个引用?

不,不总是。

另一种方法是使 Inner return 的所有设置器成为 Inner 的新实例,而不是设置 this 的字段。通常这些 setter 的名称为“+”,例如 withInnerValue:

public Inner withInnerValue(int innerValue) {
    return new Inner(innerValue);
}

然后在测试中,你将被迫改为这样做:

void testEncapsulation() {
    Inner inner = new Inner(3);
    Outer outer = new Outer(inner);
    inner = inner.withInnerValue(4); <---- this is forced to change
    assertEquals(3, outer.getInner().getInnerValue());
}

通过重写 Inner 的设置器,您已将其设为 不可变,也就是说,一旦实例创建,其字段便无法更改。

保持简单,无需克隆等:

class OuterTest {
    @Test
    void testEncapsulation() {
        Inner inner = new Inner(3){{
          setInnerValue(4);
        }};
        Outer outer = new Outer(inner);
        assertEquals(3, outer.getInner().getInnerValue());
    }
}