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

How to implement immutable and read-only versions of mutable objects effectively?

上下文:

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

(当然,解决这个问题的方法是克隆被引用的字符串。)

编辑:如果有的话我会放一个偏执的标签

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 实现 _DataDataHolder 当然可以,不是吗?毕竟它有一个 String getData() 方法! DataHolder 将有一个 asReadOnly 方法来创建 this 和 returns _Data 的副本。事实上,我会将 _Data 重命名为 ReadOnlyData.