java - 克隆或不可修改的集合

标签 java clone immutability private-members

在大学 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/

相关文章:

jQuery,获取所有克隆输入字段的所有值

jquery - 当我制作一个可拖动克隆并将其放入可放置对象中时,我无法再次拖动它

constants - 如何通过 D 中的 const 引用循环遍历图形?

c# - 有没有一种简单的方法来制作类的不可变版本?

java - 无法为 JSP : cannot be resolved to a type 编译类

java - Hadoop MapReduce : Strange Result when Storing Previous Value in Memory in a Reduce Class (Java)

java - 重新安装Java JDK后无法在ubuntu上打开eclipse

java - Spring MVC @RequestParam 和 @SessionAttribute

javascript - React 使用扩展运算符向克隆元素添加新的 props

f# - 是否可以在不放弃不变性的情况下更新 RavenDB 中的 F# 实体?