不可变对象和不可修改的集合

时间:2012-09-16 21:47:10

标签: java design-patterns collections immutability

使用不可变对象,将包含的集合包装为不可修改的位置是正确的?我看到3个选项:

  1. 在不可变对象的工厂中:

    public class ImmutableFactory {
    
        public Immutable build(){
           List<Integer> values = new ArrayList<Integer>();
    
            values.add(1);
            values.add(2);
            values.add(3);
    
            return new Immutable(Collections.unmodifiableList(values), "hello");
        }
    }
    
  2. 在immutable的构造函数

    public class Immutable {
    
        private final List<Integer> values;
        private final String hello;
    
        public Immutable(List<Integer> values, String hello) {
            this.values = Collections.unmodifiableList(values);
           this.hello = hello;
        }
    
        public List<Integer> getValues() {
            return values;
        }
    
        public String getHello() {
            return hello;
        }
    }
    
  3. 在immutable的访问者中(如果适用)。

    public class Immutable {
    
        private final List<Integer> values;
        private final String hello;
    
        public Immutable(List<Integer> values, String hello) {
            this.values = values;
            this.hello = hello;
        }
    
        public List<Integer> getValues() {
            return Collections.unmodifiableList(values);
        }
    
        public String getHello() {
            return hello;
        }
    }
    
  4. 还有其他选择吗?哪一种适当?

2 个答案:

答案 0 :(得分:4)

我会说如果你创建了Immutable集合,那么你需要保护自己免受某人修改作为构造函数参数的列表的影响,你应该保护性地复制它。

public Immutable(List<Integer> values, String hello) {
    this.values = Collections.unmodifiableList(new ArrayList<Integer>(values));
    this.hello = hello;
}

我个人很久以前就切换到了Guava个集合,因为你可以找到不可变集合的接口。你的构造函数看起来像那样:

public Immutable(ImmutableList<Integer> values, String hello) {
    this.values = values;
    this.hello = hello;
}

你确定你收到的论点不会被任何人修改。

在不可变类中保留对不可变列表的引用通常是个好主意,因为它可以保证你不会错误地修改它。

我认为Case(1)只有当工厂方法是创建不可变集合的唯一方法时才有意义,即使这样,当有人重构这些类时它也可能会中断。除非有其他限制,否则最好创建自给自足的类。

答案 1 :(得分:1)

首先,您所有地方的代码确实是不可变的只是因为Integer是不可变的。
如果您改为使用private final List<SomeCustomClass> values;,则Collections.unmodifiableList(values);无法保证列表中的个别SomeCustomClass是不可变的。 必须为此编码。
话虽如此,另一种选择是对列表进行 defencive deep copy 。这种方法也提供了不变性。