【问题标题】:How to make the below java class Immutable如何使下面的java类不可变
【发布时间】:2015-05-15 19:09:24
【问题描述】:

除了以下之外,还有什么其他方法可以使下面的类不可变。

  • 没有二传手
  • 所有字段都是私有的和最终的
  • 类被声明为final,因此子类中的方法不能被覆盖

    public final class ImmutableClass {
    
        private final String name;
    
        private final List<Integer> listOfNumbers;
    
        public ImmutableClass(String name, List<Integer> listOfNumbers) {
            this.name = name;
            this.listOfNumbers = listOfNumbers;
        }
    
        public String getName() {
            return name;
        }
    
        public List<Integer> getListOfNumbers() {
            return listOfNumbers;
        }
    }
    

【问题讨论】:

  • this.listOfNumbers = Collections.unmodifiableList(new ArrayList&lt;&gt;(listOfNumbers));
  • 您的列表是可变的。返回一个不可变的版本或副本。

标签: java immutability


【解决方案1】:

是的。您需要对构造函数中提供的列表和 getter 中提供的列表进行防御性复制。或者,使用 Google Guava 的 ImmutableList 类,您可以将复制保存在 getter 中。

public ImmutableClass(String name, List<Integer> listOfNumbers) {
    this.name = name;
    // you need to null check your parameter here first
    this.listOfNumbers = ImmutableList.copyOf(listOfNumbers);
}

这确保了 getter 返回的对象不会被客户端篡改,即使它与您存储在字段中的对象实例相同。

如果你真的想学究气,你仍然可以这样写你的getter,开销相对较小:

public List<Integer> getListOfNumbers() {
    return ImmutableList.copyOf(listOfNumbers);
}

作为ImmutableList.copyOf() will try to avoid making a copy when it's safe to do so,这实际上不会创建新副本,因此放入它没有多大意义。

P.s.:根据您可能想要强制执行的任何先决条件检查构造函数中的输入参数也是一种好习惯。 (例如,列表不能为空,也不能为空。)这些检查应该始终在副本上进行,除了需要在创建副本之前进行的空检查。但重点不是不变性,而是编写安全的代码来维护其不变量,但客户端试图破坏它们。

【讨论】:

    【解决方案2】:

    您应该防御性地复制传递给构造函数的listOfNumbers,并在getter 中返回它的不可变视图。

    【讨论】:

      【解决方案3】:

      listOfNumbers 需要在构造函数中复制,列表的 getter 需要返回列表的副本。就目前而言,您返回的可变列表违反了类的不变性。

      或者,您可以使用不可变列表实现,例如来自Guava的那个。

      【讨论】:

      • 在构造函数中也要进行复制也很重要。
      【解决方案4】:

      使用Collections.unmodifiableList

      public List<Integer> getListOfNumbers() {
          return Collections.unmodifiableList(listOfNumbers);
      }
      

      【讨论】:

        【解决方案5】:

        看看 Joshua Bloch 的书 Effective Java,特别是第 15 条。

        他对如何使类不可变给出了惊人的解释。

        【讨论】:

          【解决方案6】:

          由于其他人已经正确回答,您需要确保没有人可以修改listOfNumbers字段。

          但是,您可以使用我编写的名为 Mutability Detector 的自动化工具得到相同的答案,当您想要测试其他想要使其不可变的类时,它可能会派上用场。

          鉴于您的ExampleClass,以及以下单元测试:

          import org.junit.Test;
          import static org.mutabilitydetector.unittesting.MutabilityAssert.assertImmutable;
          
          public class Question_30240358 {
          
              @Test
              public void isImmutable() {
                  assertImmutable(ImmutableClass.class);
              }
          }
          

          结果是单元测试失败,并显示以下消息:

          Expected: org.mutabilitydetector.stackoverflow.ImmutableClass to be IMMUTABLE
               but: org.mutabilitydetector.stackoverflow.ImmutableClass is actually NOT_IMMUTABLE
              Reasons:
                  Attempts to wrap mutable collection type using a non-whitelisted unmodifiable wrapper method. [Field: listOfNumbers, Class: org.mutabilitydetector.stackoverflow.ImmutableClass]
              Allowed reasons:
                  None.
                  at org.mutabilitydetector.unittesting.internal.AssertionReporter.assertThat(AssertionReporter.java:48)
                  at org.mutabilitydetector.unittesting.MutabilityAsserter.assertImmutable(MutabilityAsserter.java:108)
                  at org.mutabilitydetector.unittesting.MutabilityAssert.assertImmutable(MutabilityAssert.java:672)
                  at org.mutabilitydetector.stackoverflow.Question_30240358.isImmutable(Question_30240358.java:14)
          

          如果字段分配更改为:则此测试将通过:

          this.listOfNumbers = Collections.unmodifiableList(new ArrayList<Integer>(listOfNumbers));
          

          该测试将捕获许多其他类型的引入可变性的问题。

          【讨论】:

          • 谢谢。这将非常有用。
          猜你喜欢
          • 2018-10-19
          • 2017-11-23
          • 1970-01-01
          • 1970-01-01
          • 2015-10-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多