例如,我有这个:
public class Container{
private final List<String> strs;
/**
* Contructs {@code Container} by a given {@code List}
* The content of the list referenced should not be modified
* after the constructor invocation.
*/
public Container(List<String> strs){
this.strs = strs;
}
//Other staff
}
Container
的状态可以在构建后修改,但我们在文档中禁止。该对象可以被视为不可变的吗?在构造函数中进行复制并不是完全可取的。
不变性是针对线程安全的。
答案 0 :(得分:3)
如果您不想复制列表,但仍希望它真正不可变,则可以使用persistent data structure之类的Clojure。
在Clojure中,所有值都是不可变的,因此您可以随时安全地传递它们。当有人将一个项目添加到列表的前面时,这会逻辑上创建一个新列表,但实际上它只添加一个指向旧列表头部的元素,而不必复制所有以前的列表。 Clojure中的所有数据结构都是这样的,您也可以在Java中使用它们(另请参阅what's a good persistent collections framework for use in java?)。
或者,您可以使用pcollections。
答案 1 :(得分:2)
通常,您不应将类的内部结构暴露给外部。这对于imutables来说非常重要。
因此,我建议您在构造函数中使用new ArrayList(x)
制作副本。
此外,您可以使用Collections.unmodifiableList()
来防止来自班级内部的修改。
我建议两者结合,看起来像这样:
import java.util.Collections;
...
public Container(List<String> strs){
this.strs = Collections.unmodifiableList(new ArrayList<>(strs));
}
您的容器将在调用构造函数时记住列表的成员。如果列表在其他地方被更改,它将不会对您的imutable产生任何影响。
即使容器中的代码也无法修改列表 - 列表会抛出UnsupportedOperationException
。
完整的,有效的示例代码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class X {
public static void main(String[] args) {
// create a list
List<String> myList = new ArrayList<>();
myList.add("a");
myList.add("b");
// hand it over to the container
Container container = new Container(myList);
// modify it afterwards
myList.add("BUH!");
// check contents of container
for (String item : container.strs) {
System.out.println(item);
}
}
}
class Container{
final List<String> strs;
/**
* Contructs {@code Container} by a given {@code List}
* The content of the list referenced should not be modified
* after the constructor invokation.
*/
public Container(List<String> strs){
this.strs = Collections.unmodifiableList(new ArrayList<>(strs));
}
//Other staff
}
输出:
a
b
(不输出BUH!
)
答案 2 :(得分:2)
您可以记录它以提高性能,但我只建议使用非公共API的方法。在我的公司中,我们构建了一个框架,并设法取消一些这些保护以提高性能/减少内存使用,但仅限于内部代码。我们决定假设框架开发人员不会陷入困境并且运行良好。
如果您的API将会暴露给外面的其他开发人员,那么您应始终保持安全,特别是出于安全考虑。如果您真的需要提高性能/内存使用率,那么这是一个选项,但请谨慎应用。
答案 3 :(得分:2)
您决定允许这样的结构是保证正确性和性能之间的权衡。
构建器模式可能为您提供第三种处理方式(取决于它是否适用于您的列表构造):
构造Container
创建一个Builder
,它收集列表元素,然后最终调用(私有)Container构造函数。这样可以方便地收集列表元素,并避免在构造最终对象时复制列表:
public class Container {
public static class Builder {
public Builder add(String s) { strs.add(s); return this; }
public Container create() {
Container c = new Container(Collections.unmodifiableList(strs));
strs = null;
return c;
}
private List<String> strs = new ArrayList<>();
}
private final List<String> strs;
private Container(List<String> strs){
this.strs = strs;
}
}
答案 4 :(得分:1)
一般来说,没有。可以在线程之间安全地共享不可变对象。在线程之间共享的安全对象不仅仅是文档问题;它的类必须以特定的方式实现。说&#34;这个类的对象是不可变的&#34;没有让它变得不可变的实现会积极误导,不真实,应该被视为一个bug。