【发布时间】:2013-10-24 21:32:11
【问题描述】:
在Efficient Java Joshua Bloch 写道:
注意一个非零长度的数组总是可变的,所以它是错误的 一个具有公共静态最终数组字段的类,或一个访问器 返回这样的字段。如果一个类有这样的字段或访问器,客户端 将能够修改数组的内容。这是一个经常 安全漏洞的来源:
// Potential security hole!
public static final Thing[] VALUES = { ... };
请注意,许多 IDE 生成的访问器会返回 对私有数组字段的引用,导致了这个问题。 有两种方法可以解决问题。您可以制作公共数组 私有并添加一个公共不可变列表:
private static final Thing[] PRIVATE_VALUES = { ... };
public static final List<Thing> VALUES =
Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
或者,您可以将数组设为私有并添加公共方法 返回私有数组的副本:
private static final Thing[] PRIVATE_VALUES = { ... };
public static final Thing[] values() {
return PRIVATE_VALUES.clone();
}
我的问题是:
- 为什么还要返回
final变量 - 如果它只是一个副本?
毕竟,如果用户想要修改它(供她/他自己使用),我们实际上是在强迫她/他创建另一个非最终副本,这是没有意义的。
【问题讨论】:
-
好的,我想我明白你在说什么了。你问为什么要返回一个不可修改的列表而不是
int[] ret = new int[sce.length]; System.arraycopy(sce, 0, ret, 0, sce.length); return ret;?这对于接收者来说是可变的,但不引用您不想修改的原始字段。 -
在这种情况下,我喜欢 Guava 的
Immutable*类;紧凑、高性能、不可变。 -
@Radiodef 是的 - 这正是我的意思! Boann 提供了一个很好的答案 - 这不是原始列表的副本,只是限制写入访问的“视图”。当我阅读文档时,我错过了“视图”的含义......
-
@Radiodef 但现在说得通了。与复制列表相比,返回“视图”应该便宜。它“保存”了复制操作 - 如果她/他想要/需要这样做,则将责任转移给用户。
-
@alfasin 是的。这是一种很好的技术,因为它创建的集合显然包含大量数据,但它们具有边际创建成本,因为它们几乎不存储任何内容并避免不必要的复制。另一方面,包装器在每次访问时总是有更多的开销,因为每个访问器方法都必须将调用转发给被包装对象上的相应方法。当然,如果对返回对象的访问性能很关键,仍然可以对其调用
toArray()或以其他方式创建数据的更直接副本。