使用Java中的getter方法返回私有集合

我有许多在内部使用私有集或列表的Java类。 我希望能够使用get … List()方法返回这些集/列表。

我正在考虑的替代方案:

  1. 返回对内部对象的引用
  2. 构建一个新的集/列表并填写它(这似乎是不好的做法?)
  3. 使用Collections.unmodifiableList(partitions);

以下哪项是解决此问题的最常见/最佳方式?

这里有许多方面需要考虑。 正如其他人已经指出的那样,最终的决定取决于你的意图,但有关这三个选项的一般性陈述:

1.返回对内部对象的引用

这可能会带来问题。 当你这样做时,你几乎不可能保证一致的状态。 调用者可能会获取列表,然后做一些讨厌的事情

 List list = object.getList(); list.clear(); list.add(null); ... 

也许不是出于恶意,而是偶然,因为他认为安全/允许这样做。


2.构建一个新的集合/列表并填写它(这看起来很糟糕吗?)

这不是一般的“坏习惯”。 无论如何,它是API设计方面迄今为止最安全的解决方案。 这里唯一需要注意的是,可能存在性能损失,具体取决于几个因素。 例如,列表中包含多少元素,以及如何使用返回的列表。 像这样的一些(可疑的?)模式

 for (int i=0; i 

可能会变得非常昂贵(虽然有人可能会争辩说,在这种特殊情况下,是否是这样实现它的用户的错, 一般问题仍然有效)


3.使用Collections.unmodifiableList(partitions);

这是我个人经常使用的。 它在API设计意义上是安全的,与复制列表相比,其开销只有微不足道。 但是,调用者在获得对它的引用之后知道该列表是否可能发生变化是很重要的。

这导致...


最重要的建议:

记录方法正在做什么! 不要写这样的评论

 /** * Returns the list of elements. * * @return The list of elements. */ public List getList() { ... } 

相反,请指定您可以确定列表的内容。 例如

 /** * Returns a copy of the list of elements... */ 

要么

 /** * Returns an unmodifiable view on the list of elements... */ 

就个人而言,我总是在这种文档的两个选项之间徘徊:

  • 明确该方法正在做什么以及如何使用
  • 不要暴露或过度指定实现细节

例如,我经常写这样的文档:

 /** * Returns an unmodifiable view on the list of elements. * Changes in this object will be visible in the returned list. */ 

第二句是关于行为的明确而有约束力的陈述。 调用者知道这一点非常重要 。 对于并发应用程序(并且大多数应用程序以一种方式或另一种方式并发),这意味着调用者必须假定列表在获得引用后可能同时更改,这可能会导致在发生更改时发生ConcurrentModificationException正在迭代列表。

但是,这些详细的规范限制了之后改变实现的可能性。 如果您以后决定返回内部列表的副本,则行为将以不兼容的方式更改。

所以有时候我也明确指出没有指定行为:

 /** * Returns an unmodifiable list of elements. It is unspecified whether * changes in this object will be visible in the returned list. If you * want to be informed about changes, you may attach a listener to this * object using this-and-that method... */ 

当您打算创建公共API时,这些问题主要是不重要的。 一旦你以某种方式实现它,人们就会以一种或另一种方式依赖这种行为。

所以回到第一点:它总是取决于你想要达到的目标。

你的决定应该基于一件事(主要是)

  1. 允许其他方法修改原始集合?

    是:返回内部对象的引用。

    1. 构建一个新的集合/列表并填写它(这似乎是不好的做法? – 不是。完全没有。这被称为防御性编程并被广泛使用)。
    2. 使用Collections.unmodifiableList(partitions);
 return a reference to the internal object 

在这种情况下,接收方端可以修改对象的集合或列表,这可能不是必需的。 如果允许用户修改对象的状态,那么这是最简单的方法。


 construct a new set/list and fill it up (this seems bad practice?) 

这是示例浅拷贝,其中集合对象不可修改,但对象将使用相同。 因此,对象状态的任何更改都将影响实际的集合。


 use Collections.unmodifiableList(partitions); 

在这种情况下,它返回指定列表的不可修改视图。 此方法允许模块为用户提供对内部列表的“只读”访问。 在您希望保持对象状态安全的情况下,这可以用作最佳实践。

我相信最好的解决方案是返回一个不可修改的列表。 如果与新列表的构造相比较,则返回原始列表的不可修改的“代理”可以使客户端不会隐式地生成许多不必要的列表。 另一方面,如果客户端确实需要具有可修改的列表,则让它自己创建一个新列表。

您仍需要考虑的问题是可能会修改列表中包含的对象。 Java中没有便宜且简单的const-correctness。

第二种选择绝对是正确的方法。

其他两个选项取决于您的要求。

  • 如果您不打算修改类外的列表值,请返回不可修改的列表。
  • 否则,只需返回参考。