除了以下内容之外,还有什么方法可以使下面的类不可变。
将类声明为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;
}
}
答案 0 :(得分:6)
你应该防御 - 复制传递给构造函数的listOfNumbers
并在getter中返回它的不可变视图。
答案 1 :(得分:6)
是。您需要制作构造函数中提供的列表的防御副本以及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 :(得分:2)
listOfNumbers
需要在构造函数中复制,列表的getter需要返回列表的副本。目前,你正在返回一个违反班级不变性的可变列表。
或者,您可以使用不可变列表实现,例如来自Guava的那个。
答案 3 :(得分:2)
使用Collections.unmodifiableList
public List<Integer> getListOfNumbers() {
return Collections.unmodifiableList(listOfNumbers);
}
答案 4 :(得分:2)
看看Joshua Bloch的书Effective Java,特别是第15项。
他给出了关于如何使类不可变的惊人解释。
答案 5 :(得分:1)
正如其他人已正确回答,您需要确保没有人可以修改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));
该测试将捕获许多其他引入可变性的问题。