我是否总是需要克隆对象以确保Java中的封装?
给定一个外部类,该类将对内部类的对象的引用作为参数:我是否总是需要克隆对象以确保Java中的封装?,java,encapsulation,Java,Encapsulation,给定一个外部类,该类将对内部类的对象的引用作为参数: public class Outer { private Inner inner; public Outer(Inner inner) { // fails // this.inner = inner; // passes this.inner = this.clone(inner); } public Inner getInner() {
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());
}
}
内部类只有一个整数值
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());
}
}
仅当克隆内部see注释失败时通过。一般来说是这样吗?所以,每当我通过一个引用时,我是否需要克隆每个引用?不,不一定
另一种方法是让所有的内部设置器返回一个新的内部实例,而不是设置这个实例的字段。通常,这些设置器的名称为+,例如withInnerValue:
然后在测试中,您将被迫这样做:
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());
}
通过重写Internal的setters,可以使其不可变,即创建实例后其字段不能更改。在不使用克隆等操作的情况下保持简单:
class OuterTest {
@Test
void testEncapsulation() {
Inner inner = new Inner(3){{
setInnerValue(4);
}};
Outer outer = new Outer(inner);
assertEquals(3, outer.getInner().getInnerValue());
}
}
如果您的内部实例设计为克隆,则可以放弃克隆。有趣的事实:因为它不是不可变的,所以当您编写这样的setter时,sonar将产生一个警告:public void setDatefinal Date{this.Date=Date;}而不是public void setDatefinal Date{this.Date=new Datedate.getTime;}。旁注:您的getter方法与您将内部状态泄漏给外部世界的方法有相同的问题。您能解释一下这些setter的名称通常为+,例如withInnerValue:?我不太明白你在这里的意思。但是有趣的方法。。您的withInnerValue看起来几乎像我的Outer.clone。@t如果您查看java.time包中的类,您会看到很多这样的setter。LocalDateTime.atOffset、LocalDateTime.atZone、LocalDateTime.withSecond等。与克隆不同的是,我在值更改时创建了一个新对象,而不是在传入值时在开始时创建。@TMOTTM是的。如果使用克隆方法,则必须记住在返回对象或将其传递到其他地方之前克隆对象。但对于不可变对象,您必须创建新对象。如果你不这样做,编译器会抱怨的。那么数百万自动生成的setter方法就一文不值了?或者他们可以自己调用InnerValueInnerValue来设置值吗?@tMotm可变对象也有自己的位置。有关反驳,请参阅。
class OuterTest {
@Test
void testEncapsulation() {
Inner inner = new Inner(3){{
setInnerValue(4);
}};
Outer outer = new Outer(inner);
assertEquals(3, outer.getInner().getInnerValue());
}
}