克隆或不可修改的收集

时间:2013-04-17 22:40:06

标签: java clone immutability private-members

在我的Java大学课程中,我了解了隐私泄漏的概念,其中公共getter返回对私有可变对象的引用。

他们给出的例子如下:(如果语法不正确,请原谅我,我在记忆中工作)

private HashSet<*> priv = new HashSet<*>

public Collection<*> getPriv () {
    return this.priv;
}

现在问题是对象的客户端现在有机会有意或无意地破坏私有HashSet。

在课程中,建议的解决方案是使用以下内容:

public Collection<*> getPriv () {
    return Collections.unmodifiableCollection (this.priv);
}

这对我来说似乎是一个很好的解决方案,因为不仅保护对象的内部状态不受外部修改,任何修改返回的unmodifiableCollection的尝试都会触发异常。但问题是,它只适用于集合。似乎没有其他可变Java类的等价物。

我读过的大多数有关Java的书籍都提出了以下建议:

public Collection<*> getPriv () {
    return this.priv.clone ();
}

这适用于任何可克隆对象,但返回的对象可以修改为客户端对象的内容。对象的内部状态仍然受到保护,但是如果我尝试修改无法真正修改的数据,现在我不会得到异常。这意味着我可能会犯一个错误,我得到私人收藏,修改它,并且为什么我的更改没有反映在对象中而感到迷惑,所以我换了一类错误(修改私有状态)给另一个(修改应该是不可变的克隆)。

是否有一种更通用的方式来执行return Collections.unmodifiableCollection (this.priv);,它可以用于任何类(或者至少任何满足某些先决条件的类,例如实现cloneable),或者是我实现可变和不可变版本的唯一选择我可能希望通过getter公开提供的所有私人课程?

1 个答案:

答案 0 :(得分:3)

我担心答案是肯定的。

请注意,Collections.unmodifiableCollection仅返回一个视图。从本质上讲,集合包含一个适配器类,它接收任何Collection并转发对它的某些方法调用。对于“危险”方法,它会引发异常。这依赖于传入的底层集合,当然是以理智的方式实现的。如果getSize()改变了对象,那么在适配器上调用getSize()仍然会将它转发给基础类,然后修改它。

您可以通过限制返回客户端的不同类(或接口)的数量来减少工作量。默认情况下,您还可以使更多的类不可变,这是函数式语言中的一种常见范例,它使事情变得更加简单。不幸的是,Java使得处理不可变对象变得不必要的丑陋。

还有一些可能性,例如自动生成不可变视图,但这很复杂,并且要求您有办法决定转发哪些方法。

最后,这些都不能保护您免受setAccessible之类的伤害。但在这种情况下,他们故意违反了标准的虚拟机限制,所以这不是你应该担心的事情。如果您担心安全问题,则无论如何都需要在SecurityManager中运行所有不受信任的代码。