Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我是否总是需要克隆对象以确保Java中的封装?_Java_Encapsulation - Fatal编程技术网

我是否总是需要克隆对象以确保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());
    }
}