Java10个copyOf工厂方法是否适合构造函数中的防御副本
Are the Java 10 copyOf factory methods suitable for defensive copies in constructors
当实现一个 immutable class 时,它有一个 aggregate 由给定构造函数的 Collection
初始化,构造函数必须制作 Collection
的防御性副本,以防止调用者随后改变聚合。
在 Java 10 之前,这将导致代码如下:
class Thing
{
private final List<Widget> widgets;
public Thing(List<Widget> widgets) {
this.widgets = Collections.unmodifiableList(new ArrayList<>(widgets));
}
public List<Widget> getWidgets() {
return widgets;
}
}
Java 10 添加了 List.copyOf()
和朋友,它们创建了给定 Collection
的不可修改的副本。这表明对于 Java 10+,我们可以改为编写如下代码:
class Thing
{
private final List<Widget> widgets;
public Thing(List<Widget> widgets) {
this.widgets = List.copyOf(widgets);
}
public List<Widget> getWidgets() {
return widgets;
}
}
我说得对吗?
设计意图和实现List.copyOf
和其他copyOf
方法是完全按照你的意愿去做,应该是合适的用于制作防御副本。这个想法是List.copyOf
总是复制,除非它能证明不复制是安全的。
来自 List.copyOf
specification 的其他上下文是:
Returns an unmodifiable List containing the elements of the given Collection, in its iteration order. The given Collection must not be null, and it must not contain any null elements. If the given Collection is subsequently modified, the returned List will not reflect such modifications.
Implementation Note:
If the given Collection is an unmodifiable List, calling copyOf will generally not create a copy.
关键断言是第一段的最后一句话,它说如果参数随后被修改,则该修改不会出现在返回的列表中。也就是说,返回的列表在概念上始终是一个副本。
另请注意,“不可修改的列表”是列表规范特定部分的 link,其中描述了 List.of
、List.copyOf
和流的 Collectors.toUnmodifiableList
.
Collections.unmodifiableList
返回的实例在此定义中不是“不可修改的列表”。相反,它是一个 unmodifiable view。此类视图明确允许修改后备集合,前提是有人引用了后备集合,并且该集合本身是可修改的。
是的,“不可修改的列表”和“不可修改的视图”之间的区别令人困惑,但这是我们确定的。对不起。有一个替代方案是“不可变的”。但这是不准确的,因为这些实例实际上是可变的,因此我们避免在这些情况下使用“不可变”。
无论如何,实际行为反映了这个规范:
var list1 = new ArrayList<String>(Arrays.asList("a", "b", "c"))
var list2 = Collections.unmodifiableList(list1)
var list3 = List.copyOf(list2)
var list4 = List.copyOf(list3)
list2 == list3
==> false
list3 == list4
==> true
list1.add("d")
list2
==> [a, b, c, d]
list3
==> [a, b, c]
换句话说,List.copyOf
制作不可修改视图的防御性副本,并且此行为是规范所要求的。
当实现一个 immutable class 时,它有一个 aggregate 由给定构造函数的 Collection
初始化,构造函数必须制作 Collection
的防御性副本,以防止调用者随后改变聚合。
在 Java 10 之前,这将导致代码如下:
class Thing
{
private final List<Widget> widgets;
public Thing(List<Widget> widgets) {
this.widgets = Collections.unmodifiableList(new ArrayList<>(widgets));
}
public List<Widget> getWidgets() {
return widgets;
}
}
Java 10 添加了 List.copyOf()
和朋友,它们创建了给定 Collection
的不可修改的副本。这表明对于 Java 10+,我们可以改为编写如下代码:
class Thing
{
private final List<Widget> widgets;
public Thing(List<Widget> widgets) {
this.widgets = List.copyOf(widgets);
}
public List<Widget> getWidgets() {
return widgets;
}
}
我说得对吗?
设计意图和实现List.copyOf
和其他copyOf
方法是完全按照你的意愿去做,应该是合适的用于制作防御副本。这个想法是List.copyOf
总是复制,除非它能证明不复制是安全的。
来自 List.copyOf
specification 的其他上下文是:
Returns an unmodifiable List containing the elements of the given Collection, in its iteration order. The given Collection must not be null, and it must not contain any null elements. If the given Collection is subsequently modified, the returned List will not reflect such modifications.
Implementation Note: If the given Collection is an unmodifiable List, calling copyOf will generally not create a copy.
关键断言是第一段的最后一句话,它说如果参数随后被修改,则该修改不会出现在返回的列表中。也就是说,返回的列表在概念上始终是一个副本。
另请注意,“不可修改的列表”是列表规范特定部分的 link,其中描述了 List.of
、List.copyOf
和流的 Collectors.toUnmodifiableList
.
Collections.unmodifiableList
返回的实例在此定义中不是“不可修改的列表”。相反,它是一个 unmodifiable view。此类视图明确允许修改后备集合,前提是有人引用了后备集合,并且该集合本身是可修改的。
是的,“不可修改的列表”和“不可修改的视图”之间的区别令人困惑,但这是我们确定的。对不起。有一个替代方案是“不可变的”。但这是不准确的,因为这些实例实际上是可变的,因此我们避免在这些情况下使用“不可变”。
无论如何,实际行为反映了这个规范:
var list1 = new ArrayList<String>(Arrays.asList("a", "b", "c"))
var list2 = Collections.unmodifiableList(list1)
var list3 = List.copyOf(list2)
var list4 = List.copyOf(list3)
list2 == list3
==> false
list3 == list4
==> true
list1.add("d")
list2
==> [a, b, c, d]
list3
==> [a, b, c]
换句话说,List.copyOf
制作不可修改视图的防御性副本,并且此行为是规范所要求的。