我的一个类(class)有一个包含集合的字段。该字段仅在构造函数中填充,然后由其他类读取。最初我有这样的东西:
public class Foo {
public final Set<String> myItems;
public Foo(Collection<String> theirItems) {
this.myItems = new LinkedHashSet<String>(theirItems);
}
}
但这违背了 OO 最佳实践,myItems 应该是私有(private)的,并且只能通过 setter 和 getter 访问。所以后来我把它改成了:
public class Foo {
private final Set<String> myItems;
public Foo(Collection<String> theirItems) {
this.myItems = new LinkedHashSet<String>(theirItems);
}
public Set<String> getItems() {
return myItems;
}
}
现在 myItems 是私有(private)的,但是调用 getItems() 的人仍然可以随意添加/删除项目,这与我之前的情况基本相同。 (我实际上并不担心有人更改项目内容,这更像是一个理论问题)
然后我更改了 getItems() 以返回一个数组:
public String[] getItems() {
return myItems.toArray(new String[myItems.size()]);
}
现在我的元素真的很私密。不幸的是,我知道将读取项目的对象实际上想要使用 Set,因此它必须立即将数组转换回来。我还可以返回 myItems 的副本:
public Set<String> getItems() {
return new LinkedHashSet<String>(myItems);
}
这会为调用者提供他们想要的内容,但会在每次访问时创建一个新的 Set。
在这种情况下你会怎么做 - 不惜一切代价保护隐私,接受原始结构的转换/复制,或者牺牲对集合内容的控制并依赖负责任的调用者?
最佳答案
将不可修改的 View 返回到您的集合:
public Set<String> getItems() {
return Collections.unmodifiableSet(myItems);
}
请注意,这意味着调用者仍然会看到您对集合所做的任何更改,如果他们卡在返回的集合上。如果您不想那样做,您将必须 制作一份副本……没有(简单的)方法可以解决这个问题。 (理论上,您可以制作一个不可修改的副本并返回对同一副本的引用,直到您下次进行更改为止,但这会变得困惑。)
重要的一点是记录您选择的任何内容,以免调用者收到任何令人讨厌的意外信息。在许多方面,我认为这实际上是大多数应用程序中最重要的事情,其中调用者实际上并不是恶意的。只要很清楚会产生什么影响,在大多数情况下,防御就没那么重要了。当然,如果您的调用者可能是一些不可信任的代码并且您的设置对安全等至关重要,那么您的情况就不同了。
关于java - 访问 Java 中的私有(private)集合字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1502025/