简体   繁体   English

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

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

Context:语境:

  • package data, public: package 数据,公开:
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();
    }
}
  • package data, package-private: package 数据,包私有:
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;
            }
        };
    }
}
  • sample client usage:示例客户端用法:
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() );



    }
}
  • And console output:和控制台 output:
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
  • FIRST QUESTION : given getSnapshot() returns a _Data (of class: DataSnapshot$1) whose method getData() returns the "real" data reference, ie the content of the variable data of the DataHolder object, is this safe or is it somehow possible to mutate DataHolder leveraging access to this reference?一个问题:给定getSnapshot()返回一个_Data(class:DataSnapshot $ 1),其方法getData()返回“真实”数据引用,即DataHolder object的变量数据的内容,这是安全的还是可能的变异 DataHolder 利用对此参考的访问? If yes, how?如果是,如何?

  • FIRST QUESTION SHORTENED : is it anyhow possible to mutate content of the memory referenced by a reference, using only the reference?缩短的第一个问题:是否可以仅使用参考来改变参考引用的 memory 的内容?

(Of course solution to this is to clone the String being referenced.) (当然解决方案是克隆被引用的字符串。)

  • SECOND QUESTION : is there anyway to mutate a DataSnapshot$1 (the "immutable" version of _Data) instance?第二个问题无论如何都要改变 DataSnapshot$1(_Data 的“不可变”版本)实例?

  • THIRD QUESTION : given DataHolder$1 (the "readOnly" version of _Data) holds internally a reference to the DataHolder instance providing it, is it safe to expose such a DataHolder$1, or is there anyway to mess with the DataHolder instance from the DataHolder$1 object?第三个问题:给定 DataHolder$1(_Data 的“只读”版本)在内部持有对提供它的 DataHolder 实例的引用,公开这样的 DataHolder$1 是否安全,或者无论如何都会弄乱 DataHolder$1 中的 DataHolder 实例object?

EDIT: I would have put a paranoïd tag if there was one编辑:如果有的话,我会放一个偏执狂的标签

is this safe or is it somehow possible to mutate DataHolder leveraging access to this reference?这是安全的还是以某种方式可以利用对该参考的访问来改变DataHolder If yes, how?如果是,如何?

Since getData returns a String , which is immutable, the caller can't change anything through the returned reference.由于getData返回一个不可变的String ,因此调用者无法通过返回的引用更改任何内容。 You can't access a DataHolder through a String either.您也不能通过String访问DataHolder Why would String know about your DataHolder class?为什么String会知道您的DataHolder class?

is there anyway to mutate a DataSnapshot$1 ?反正有变异DataSnapshot$1吗?

No, because it is an anonymous inner class that only has one method that returns a parameter.不,因为它是一个匿名的内部 class 只有一个返回参数的方法。 Parameters are passed by value , so you don't need to worry about callers changing their values on the other side.参数是按值传递的,因此您无需担心调用者会在另一端更改其值。 It's also a String , which means the callers won't be mutating the object either.它也是一个String ,这意味着调用者也不会改变 object 。

You might be asking this because you saw how initialReader has changed.您可能会问这个问题,因为您看到了initialReader的变化。 Well, since DataHolder$1 is an inner class of the mutable DataHolder , it's not really immutable even if it doesn't have any mutator methods.好吧,由于DataHolder$1是可变DataHolder的内部 class ,因此即使它没有任何修改器方法,它也不是真正不可变的。

is it safe to expose such a DataHolder$1 , or is there anyway to mess with the DataHolder instance from the DataHolder$1 object?公开这样的DataHolder$1是否安全,或者无论如何都会弄乱DataHolder$1 object 中的DataHolder实例?

There is no way to access the outer class from the inner class , so since there are no mutator methods in DataHolder$1 , you can't mutate DataHolder from the outside, with only a DataHolder$1 . 无法从内部 class 访问外部 class ,因此由于DataHolder$1中没有突变器方法,因此您无法从外部对DataHolder进行突变,只有DataHolder$1

However, if DataHolder changes, the changes will reflect on DataHolder$1 (as shown in your sample code), which I think defeats the purpose of immutability.但是,如果DataHolder发生变化,这些变化将反映在DataHolder$1上(如您的示例代码所示),我认为这违背了不变性的目的。


Here's how I would implement immutability in this scenario.这是我在这种情况下实现不变性的方法。

I would make DataHolder implement _Data .我会让DataHolder实现_Data DataHolder certainly can do this, can't it? DataHolder当然可以做到这一点,不是吗? It has a String getData() method after all!毕竟它有一个String getData()方法! DataHolder would have an asReadOnly method that creates a copy of this and returns _Data . DataHolder将有一个asReadOnly方法,该方法创建this副本并返回_Data In fact, I'd rename _Data to ReadOnlyData .事实上,我_Data重命名为ReadOnlyData

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM