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 开销 - 以及 很多 更简单的代码。
如果我想确保在调用 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 开销 - 以及 很多 更简单的代码。