【问题标题】:Why declare a copy of a private field as `final`为什么将私有字段的副本声明为`final`
【发布时间】: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() 或以其他方式创建数据的更直接副本。

标签: java oop final


【解决方案1】:

Arrays.asList 包装原始数组。它不会复制数据。 Collections.unmodifiableList 还包装了原始列表,而不是复制数据。

这就是您返回 unmodifiableList 包装器的原因,因为否则,对 Arrays.asList 返回的列表所做的更改将写入原始私有数组。

【讨论】:

  • 谢谢,这回答了我的问题!我认为它会创建原始列表的 副本 - 我的错。
  • 因此,换句话说,返回一个 unmodifiableList 完全避免了复制列表的需要。 (假设接收方法只需要从中读取。)
  • @Boann 谢谢!我不知道。
【解决方案2】:

这不是返回一个final 对象——它只是将方法声明为不可覆盖。没有final 对象之类的东西——只有一个最终变量(引用或原语)、一个最终方法和一个最终类。

【讨论】:

  • 我的意思是一个最终变量(unmodifiableList)。我的错。为什么要返回一个不可变对象,为什么我们不应该重写这个方法?
  • 不是你不应该,而是你不能。
  • @JeroenVannevel 我不明白你的评论。我重新编辑了这个问题,这样会更清楚。
  • @alfasin 从技术上讲,您不能真正返回最终变量,因为只返回值。 Louis 是说方法中的 final 关键字声明该方法是不可覆盖的,它不引用返回值。在代码示例中,final 恰好出现在返回类型之前。它可以很容易地成为final public static Thing[] values()。人们可能出于各种原因将方法声明为 final。
  • @Radiodef 我用错误的术语来描述不可变对象。我在第一条评论中写了它,还重新编辑了问题。
猜你喜欢
  • 2011-10-02
  • 2011-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-29
相关资源
最近更新 更多