如何有效地实现可变对象的不可变和只读版本



上下文:

  • 包数据,公共:
public interface _Data {
public String getData();
}
public class _PackageAPI {
DataHolder holder;
public void createHolder(String data) {
holder = new DataHolder();
holder.setData(data);
}
public void mutateHolder(String data) {
holder.setData(data);
}
public _Data getSnapshot() {
return DataSnapshot.from(holder.getData());
}
public _Data getReader() {
return holder.readOnly();
}
}
  • 包数据,包私有:
class DataHolder {
private String data;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public _Data readOnly() {
return new _Data() {
@Override
public String getData() {
return DataHolder.this.data;
}
};
}
}
class DataSnapshot {
public static _Data from(String data){
return new _Data() {
@Override
public String getData() {
return data;
}
};
}
}
  • 示例客户端用法:
package clientPackage;
import data._Data;
import data._PackageAPI;
public class ExampleRunner {
public static void main(String[] args) {
_PackageAPI handler;
System.out.println("Creating...");
handler = new _PackageAPI();
handler.createHolder("INITIAL DATA");
System.out.println("Done creating...");
_Data initialSnapShot =  handler.getSnapshot();
_Data initialReader = handler.getReader();
System.out.println("initialSnapShot holds :" + initialSnapShot.getData() );
System.out.println("initialSnapShot class :" + initialSnapShot.getClass() );
System.out.println("initialReader class :" + initialReader.getClass() );
System.out.println("initialReader holds :" + initialReader.getData() );

System.out.println("Mutating...");
handler.mutateHolder("MUTATED DATA");
_Data subsequentSnapShot =  handler.getSnapshot();
_Data subsequentReader = handler.getReader();
System.out.println("Done mutating...");

System.out.println("initialSnapShot holds :" + initialSnapShot.getData() );
System.out.println("initialReader holds :" + initialReader.getData() );
System.out.println("subsequentSnapShot holds :" + subsequentSnapShot.getData() );
System.out.println("subsequentReader holds :" + subsequentReader.getData() );

}
}
  • 和控制台输出:
Creating...
Done creating...
initialSnapShot holds :INITIAL DATA
initialSnapShot class :class data.DataSnapshot$1
initialReader class :class data.DataHolder$1
initialReader holds :INITIAL DATA
Mutating...
Done mutating...
initialSnapShot holds :INITIAL DATA
initialReader holds :MUTATED DATA
subsequentSnapShot holds :MUTATED DATA
subsequentReader holds :MUTATED DATA
  • 第一个问题:给定getSnapshot((返回一个_Data(类:DataSnapshot$1(,其方法getData((返回"真实"数据引用,即DataHolder对象的变量数据的内容,这是安全的吗?还是有可能利用对该引用的访问来变异DataHolder?如果是,如何

  • 第一个问题简短:是否有可能仅使用引用来更改引用所引用的内存内容?

(当然,解决方案是克隆被引用的字符串。(

  • 第二个问题是否存在对DataSnapshot$1(_Data的"不可变"版本(实例进行变异的情况

  • 第三个问题:给定DataHolder$1(_Data的"readOnly"版本(在内部持有对提供它的DataHolder实例的引用,公开这样的DataHolder$1是否安全,或者是否会从DataHolder$1对象中破坏DataHolder实例

编辑:如果有一个,我会放一个paranoïd标签

这是安全的吗?还是有可能利用对该引用的访问对DataHolder进行变异?如果是,如何?

由于getData返回一个不可变的String,因此调用者不能通过返回的引用更改任何内容。您也不能通过String访问DataHolder。为什么String会知道你的DataHolder课程?

是否有突变DataSnapshot$1的方法?

否,因为它是一个匿名内部类,只有一个返回参数的方法。参数是按值传递的,因此您不必担心调用方在另一端更改其值。它也是一个String,这意味着调用者也不会改变对象。

您可能会问这个问题,因为您看到initialReader发生了怎样的变化。好吧,由于DataHolder$1是可变DataHolder的内部类,所以即使它没有任何赋值方法,它也不是真正不可变的。

公开这样的DataHolder$1是否安全,或者是否会从DataHolder$1对象中破坏DataHolder实例?

无法从内部类访问外部类,因此由于DataHolder$1中没有mutator方法,因此无法从外部对DataHolder进行突变,只能使用DataHolder$1

但是,如果DataHolder发生变化,这些变化将反映在DataHolder$1上(如您的示例代码所示(,我认为这违背了不变性的目的。


以下是我在这个场景中如何实现不变性。

我将使DataHolder实现_DataDataHolder当然能做到,不是吗?毕竟它有一个String getData()方法!CCD_ 25将具有创建CCD_ 27的副本并返回CCD_ 28的CCD_。事实上,我会将_Data重命名为ReadOnlyData

最新更新