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?
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.