在大学 Java 类(class)中,我了解了隐私泄漏的概念,其中公共(public) getter 返回对私有(private)可变对象的引用。
他们给出的例子如下:(如果语法不太正确,请原谅我,我是凭内存工作的)
private HashSet<*> priv = new HashSet<*>
public Collection<*> getPriv () {
return this.priv;
}
现在的问题是对象的客户端现在有机会有意或无意地破坏私有(private) HashSet。
在类(class)中,建议的解决方案是使用以下内容:
public Collection<*> getPriv () {
return Collections.unmodifiableCollection (this.priv);
}
这对我来说似乎是一个很好的解决方案,因为不仅可以保护对象的内部状态免受外部修改,任何修改返回的不可修改集合的尝试都将触发异常。但问题是它只适用于集合。似乎没有其他可变 Java 类的等效项。
此后我读过的大多数有关 Java 的书籍都提出了以下建议:
public Collection<*> getPriv () {
return this.priv.clone ();
}
这适用于任何可克隆对象,但返回的对象可以修改为客户端对象的内容。对象的内部状态仍然受到保护,但现在如果我尝试修改无法真正修改的数据,我不会收到异常。这意味着我可能会在获取私有(private)集合、修改它时犯一个错误,并且对为什么我的更改没有反射(reflect)在对象中感到困惑,因此我将一类错误(修改私有(private)状态)交换为另一类错误(修改应该不可变的克隆)。
是否有一种更通用的方法来执行 return Collections.unmodifyingCollection (this.priv);
,它可以与任何类(或至少满足某些先决条件(例如实现可克隆)的任何类)一起使用,或者是我实现我可能想通过 getter 公开的所有私有(private)类的可变和不可变版本的唯一选择?
最佳答案
恐怕答案是肯定的。
请注意,Collections.unmodifyingCollection 仅返回一个 View 。本质上,Collections 包含一个适配器类,它接受任何Collection 并将某些方法调用转发给它。对于“危险”方法,它会抛出异常。当然,这依赖于传入的底层集合以合理的方式实现事物。如果 getSize()
改变了对象,则在适配器上调用 getSize()
仍会将其转发到底层类,然后对其进行修改。
您可以通过限制返回给客户端的不同类(或接口(interface))的数量来减少工作量。您还可以使更多的类默认不可变,这是函数式语言中的常见范例,这使事情变得更加简单。不幸的是,Java 使得使用不可变对象(immutable对象)变得不必要的丑陋。
还有一些可能性,例如自动生成不可变 View ,但这很复杂,并且需要您有一种方法来决定转发哪些方法。
最后,这些都不能保护您免受 setAccessible
或类似的影响。但在这种情况下,他们故意违反标准虚拟机限制,因此您实际上不必担心。如果您担心安全性,则无论如何都需要在 SecurityManager 中运行所有不受信任的代码。
关于java - 克隆或不可修改的集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16071482/