Search code examples
javaencapsulation

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


Given an Outer class that takes a reference to an object of the Inner class as an argument:

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

and the Inner class that just has an integer value

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

the test

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

only passes if I clone inner (see comment fails). Is this like this in general? So do I need to clone every reference whenever I pass one?


Solution

  • No, not always.

    One other approach for doing is this is to make all setters of Inner return a new instance of Inner, instead of setting a field of this. Usually these setters will have the name " + ", such as withInnerValue:

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

    And then in the test, you will be forced to do this instead:

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

    By rewriting Inner's setters, you have made it immutable, that is, its fields can't be changed once an instance has been created.