Java: setter 应该制作“克隆”还是由调用者传递“新”副本?

Java: Is setters should make `Clone` or it's up to caller pass `new` copy?

如果我想确保在调用 setter 之后不能从外部修改对象,什么被认为是最佳和常见的做法?在代码中有详细简单的自我解释示例,有 2 个选项困境。

//caller scope
CustomObject original = new CustomObject(params...);  //original state 1
MyClass mMyClass = new MyClass(original);
original.modifyMe(params...);  //original state 2
mMyClass.setCustomObject(original);
original.modifyMe(params...); //original state 3


/*!!!REQUIREMENT: mMyClass.CustomObject should be in state 2!!!*/


class MyClass {

    private CustomObject mObject;

    public MyClass() {
        this.mObject = new CustomObject();
    }

    public MyClass(CustomObject obj) {
        this.mObject = obj.Clone();
    }

    //mObject is private, modified only through setter
    public getCustomObject() {
        return this.mObject;
    }

    public setCustomObject(CustomObject obj) {
        //Option 1 in the caller  
        //mMyClass.setCustomObject(new CustomObject(params...));
        this.mObject = obj;

        //Option 2 in the caller
        //mMyClass.setCustomObject(callerCustomObject);
        this.mObject = obj.Clone();
    }

}

我不会在这里使用克隆。与其制作低效的防御性副本,不如尝试使 CustomObject 不可变。您可以通过添加 withXXX 方法(大致相当于 setXXXX)来修改状态,但它们会创建宿主对象的新实例(而不是传入的对象)。 Project lombok 带有一些方便的预处理器注释,用于使用 Withers 创建不可变对象。另请参阅 Immutables 2.0 项目。

@AllArgsConstructor
class CustomObject {

   @Wither @Getter
   private final int state;

}

CustomObject one = new CustomObject(1);
CustomObject two = one.withState(2);

assertThat(one.getState(),equalTo(1));
assertThat(two.getState(),equalTo(2));

通过使用真正的不可变对象,与使用防御性副本相比,您将招致更少的内存(和 GC)和 CPU 开销 - 以及 很多 更简单的代码。