我正在实现一个不可变类,其结构如下:
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是一个列表。
答案 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>