如何有效地实现可变对象的不可变和只读版本?
How to implement immutable and read-only versions of mutable objects effectively?
上下文:
- 打包数据,public:
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
initialReader class :class data.DataHolder
initialReader holds :INITIAL DATA
Mutating...
Done mutating...
initialSnapShot holds :INITIAL DATA
initialReader holds :MUTATED DATA
subsequentSnapShot holds :MUTATED DATA
subsequentReader holds :MUTATED DATA
第一个问题:给定 getSnapshot() returns 一个 _Data(属于 class:DataSnapshot$1)其方法 getData() returns "real" 数据引用,即 DataHolder 对象的可变数据的内容,这安全吗,或者是否有可能利用对该引用的访问来改变 DataHolder?如果是,如何?
第一个问题缩短了:是否有可能仅使用引用来改变引用引用的内存内容?
(当然,解决这个问题的方法是克隆被引用的字符串。)
第二个问题 : 有没有改变 DataSnapshot$1(_Data 的 "immutable" 版本)实例?
第三个问题:给定 DataHolder$1(_Data 的 "readOnly" 版本)在内部保存对提供它的 DataHolder 实例的引用,公开这样一个 DataHolder$1 是否安全,或者是否存在与 DataHolder$1 对象中的 DataHolder 实例混淆的问题?
编辑:如果有的话我会放一个偏执的标签
is this safe or is it somehow possible to mutate DataHolder
leveraging access to this reference? If yes, how ?
由于getData
returns a String
是不可变的,调用者不能通过返回的引用改变任何东西。您也无法通过 String
访问 DataHolder
。为什么 String
会知道你的 DataHolder
class?
is there anyway to mutate a DataSnapshot
?
不,因为它是一个匿名内部class,只有一个方法returns一个参数。参数 are passed by value,因此您无需担心调用者在另一端更改其值。它也是一个 String
,这意味着调用者也不会改变对象。
您可能会问这个问题,因为您看到了 initialReader
的变化。好吧,因为 DataHolder
是可变 DataHolder
的内部 class,即使它没有任何修改器方法,它也不是真正不可变的。
is it safe to expose such a DataHolder
, or is there anyway to mess with the DataHolder
instance from the DataHolder
object ?
There is no way to access the outer class from the inner class,所以由于 DataHolder
中没有修改器方法,你不能从外部修改 DataHolder
,只有 DataHolder
.
但是,如果 DataHolder
发生变化,这些变化将反映在 DataHolder
上(如您的示例代码所示),我认为这违背了不变性的目的。
下面是我将如何在这种情况下实现不变性。
我会让 DataHolder
实现 _Data
。 DataHolder
当然可以,不是吗?毕竟它有一个 String getData()
方法! DataHolder
将有一个 asReadOnly
方法来创建 this
和 returns _Data
的副本。事实上,我会将 _Data
重命名为 ReadOnlyData
.
上下文:
- 打包数据,public:
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
initialReader class :class data.DataHolder
initialReader holds :INITIAL DATA
Mutating...
Done mutating...
initialSnapShot holds :INITIAL DATA
initialReader holds :MUTATED DATA
subsequentSnapShot holds :MUTATED DATA
subsequentReader holds :MUTATED DATA
第一个问题:给定 getSnapshot() returns 一个 _Data(属于 class:DataSnapshot$1)其方法 getData() returns "real" 数据引用,即 DataHolder 对象的可变数据的内容,这安全吗,或者是否有可能利用对该引用的访问来改变 DataHolder?如果是,如何?
第一个问题缩短了:是否有可能仅使用引用来改变引用引用的内存内容?
(当然,解决这个问题的方法是克隆被引用的字符串。)
第二个问题 : 有没有改变 DataSnapshot$1(_Data 的 "immutable" 版本)实例?
第三个问题:给定 DataHolder$1(_Data 的 "readOnly" 版本)在内部保存对提供它的 DataHolder 实例的引用,公开这样一个 DataHolder$1 是否安全,或者是否存在与 DataHolder$1 对象中的 DataHolder 实例混淆的问题?
编辑:如果有的话我会放一个偏执的标签
is this safe or is it somehow possible to mutate
DataHolder
leveraging access to this reference? If yes, how ?
由于getData
returns a String
是不可变的,调用者不能通过返回的引用改变任何东西。您也无法通过 String
访问 DataHolder
。为什么 String
会知道你的 DataHolder
class?
is there anyway to mutate a
DataSnapshot
?
不,因为它是一个匿名内部class,只有一个方法returns一个参数。参数 are passed by value,因此您无需担心调用者在另一端更改其值。它也是一个 String
,这意味着调用者也不会改变对象。
您可能会问这个问题,因为您看到了 initialReader
的变化。好吧,因为 DataHolder
是可变 DataHolder
的内部 class,即使它没有任何修改器方法,它也不是真正不可变的。
is it safe to expose such a
DataHolder
, or is there anyway to mess with theDataHolder
instance from theDataHolder
object ?
There is no way to access the outer class from the inner class,所以由于 DataHolder
中没有修改器方法,你不能从外部修改 DataHolder
,只有 DataHolder
.
但是,如果 DataHolder
发生变化,这些变化将反映在 DataHolder
上(如您的示例代码所示),我认为这违背了不变性的目的。
下面是我将如何在这种情况下实现不变性。
我会让 DataHolder
实现 _Data
。 DataHolder
当然可以,不是吗?毕竟它有一个 String getData()
方法! DataHolder
将有一个 asReadOnly
方法来创建 this
和 returns _Data
的副本。事实上,我会将 _Data
重命名为 ReadOnlyData
.