这个java类是不可变的吗?

时间:2012-08-23 09:42:30

标签: java immutability

我正在实现一个不可变类,其结构如下:

public final class A{
    private final B bOb;
    public A(){
        bOb = new B();
    }
    public A(A a){
        bOb = new B(a.bOb);
    }
    public A addData(Type data){ // Type - Integer,String,char,etc.
        A newA = new A(this); //making a copy of the object that is calling addData method
        newA.bOb.add(data);
        return newA;
    }
}

此实施是否正确?假设对象bOb是一个列表。

5 个答案:

答案 0 :(得分:2)

您的实施:

public A addData(int data){
    A newA = new A(this);
    newA.bOb.add(data); // <--- bOb is mutable
    return newA;
}

正在更改bOb,因此如果您公开对bOb的任何直接访问,则可以更改它。 (由于您没有公开任何有关bOb的信息,因此该方案没有意义。)

提示:

  • Collections.emptyList()返回一个不可变列表。
  • Collections.unmodifiableList()返回给定列表的不可变浅表副本。

考虑这种“更安全”的实施:

import java.util.*;

public final class A<T>{ //T must also be immutable (String, integer, char, ...)
    private final List<T> list;
    public A(){
        this.list = Collections.emptyList();
    }
    public A(List<T> list){
        this.list = Collections.unmodifiableList(list);
    }
    public A<T> addData(T data){
        List<T> shallowCopy = new ArrayList<T>(this.list);
        shallowCopy.add(data);
        return new A<T>(shallowCopy);
    }
    public List<T> getItems() {
        return this.list;
    }
}

答案 1 :(得分:2)

它不是完全不可变的,因为B本身似乎是可变的(通过add方法),A包含B的实例。

但是我认为实际上是不可变的(即它表现得好像从外部观察者的角度看它是不可变的),前提是所有以下都是真的:

  • new B(B)执行B的完整深层复制(如果不是addData可能会改变原始B内的内容)
  • 您没有通过任何其他方式泄露对bOb的引用
  • 添加的元素本身是不可变的

你可以从不可变的方面获得不变性的大部分好处所以我认为这个设计是可以的,只要Type是不可变的 - 在它构造过程中改变一个对象是好的,因为它在传递一个引用之后永远不会变异给别人一个很好的例子是java.lang.String - 在内部它包含一个可变数组,该数组在构造String时被写入但在此之后从未改变过。

答案 2 :(得分:1)

如果bOb是一个列表并且它包含可变内容,那么不是。但似乎您只使用int作为内容,这解决了问题。

答案 3 :(得分:1)

在这种形式中,是的,你的类是不可变的。

但是,这是一个简单的例子,对于的任何内容实际上都没有用,因为你无法访问任何内部数据。您需要注意的是不要让bOb的任何引用从A中逃脱。如果您添加一个返回bOb的方法,则需要返回bOb副本,以避免意外地允许调用者改变A的任何内容

答案 4 :(得分:0)

这取决于B(B b)构造函数的工作原理。如果它是复制构造函数,则应该制作b字段的深层副本。在这种情况下,A是不可变的。

相反,如果构造函数只是引用同一个b实例,那么对它的任何更改都将反映在bOb属性上,因此A类不是不可变的...... / p>