访问Java中的私有Collection字段

发布于 2024-08-05 20:53:17 字数 1217 浏览 4 评论 0原文

我的一个类有一个包含集合的字段。该字段仅在构造函数中填充,然后由其他类读取。最初我有这样的想法:

public class Foo {
    public final Set<String> myItems;
    public Foo(Collection<String> theirItems) {
        this.myItems = new LinkedHashSet<String>(theirItems);
    }
}

但这违背了 OO 最佳实践,其中 myItems 应该是私有的,并且只能通过 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 是私有的,但是无论谁调用 getItems() 仍然可以随意添加/删除项目,这基本上和我之前遇到的情况相同。 (我实际上并不担心有人更改项目内容,这更多的是一个理论问题)

所以然后我更改了 getItems() 以返回一个数组:

public String[] getItems() {
    return myItems.toArray(new String[myItems.size()]);
}

现在我的项目真的是私有的。不幸的是,我知道将读取项目的对象实际上想要使用 Set,因此它必须立即将数组转换回来。我还可以返回 myItems 的副本:

public Set<String> getItems() {
    return new LinkedHashSet<String>(myItems);
}

这为调用者提供了他们想要的内容,但在每次访问时创建一个新的 Set。

在这种情况下,您会做什么 - 不惜一切代价保护隐私,并接受原始结构的转换/复制,或者牺牲对集合内容的控制并依赖负责任的调用者?

One of my classes has a field which contains a Set. This field is only ever filled in the constructor, and then read by other classes. Originally I had something like this:

public class Foo {
    public final Set<String> myItems;
    public Foo(Collection<String> theirItems) {
        this.myItems = new LinkedHashSet<String>(theirItems);
    }
}

But this goes against OO best practices, by which myItems should be private, and only accessed via setters and getters. So then I changed it to:

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;
    }
}

Now myItems is private, but whoever calls getItems() can still add/remove items at will, which is essentially the same situation I had before. (I'm not actually concerned about somebody changing the item contents, this is more a theoretical question)

So then I changed getItems() to return an array:

public String[] getItems() {
    return myItems.toArray(new String[myItems.size()]);
}

Now my items are really private. Unfortunately, I know that the object which will read the items will actually want to work with a Set, so it would have to convert the array right back. I could also return a copy of myItems:

public Set<String> getItems() {
    return new LinkedHashSet<String>(myItems);
}

This gives the caller what they want, but creates a new Set on each access.

What do you do in a situation like this - preserve privacy at all costs, and accept the conversion/copying of the original structure, or sacrifice the control over the contents of the collection and rely on responsible callers?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

2024-08-12 20:53:18

我会选择你的最后一个选择:

public Set<String> getItems() {
    return new LinkedHashSet<String>(myItems);
}

I would go with your last option:

public Set<String> getItems() {
    return new LinkedHashSet<String>(myItems);
}
時窥 2024-08-12 20:53:18

我要么制作该集合的副本,要么使用 Collections.unmodifyingSet()。

如果性能是一个问题,我会打破封装规则并返回原始集。

I either make a copy of the set, or I use Collections.unmodifiableSet().

If performance is an issue, I break the encapsulation rule and return the original set.

九公里浅绿 2024-08-12 20:53:18

我会克隆该集以返回它,这样如果调用者修改了该集,它就不会影响您自己的集。

I would clone the set to return it so that if the caller modified the set, it wouldn't affect your own set.

深巷少女 2024-08-12 20:53:18

作为 Collections.unmodifyingSet(java.util.Set) 的替代方案,名为 Google Collections Library< /a> 有
com.google.common.collect。不可变集

As an alternative to Collections.unmodifiableSet(java.util.Set) a project called Google Collections Library has
com.google.common.collect.ImmutableSet.

旧时光的容颜 2024-08-12 20:53:17

返回一个不可修改的视图到您的集合上:

public Set<String> getItems() {
    return Collections.unmodifiableSet(myItems);
}

请注意,这意味着调用者仍然会看到您对集合所做的任何更改(如果他们保留在返回的集合上)。如果您不想这样做,您将必须制作一份副本...没有(简单)的方法可以解决这个问题。 (理论上,您可以制作一个不可修改的副本,并返回对同一副本的引用,直到您下次进行更改,但这会变得混乱。)

一个重要的一点是记录您选择的任何内容,以便调用者不会得到任何信息令人讨厌的惊喜。从很多方面来说,我认为这实际上是大多数应用程序中最重要的事情,其中​​调用者实际上并不是恶意的。只要清楚会产生什么影响,在大多数情况下,采取防御措施就不那么重要了。当然,如果您的调用者可能是一些不可信的代码,并且您的设置对于安全性等至关重要,那么您的情况就不同了。

Return an unmodifiable view onto your set:

public Set<String> getItems() {
    return Collections.unmodifiableSet(myItems);
}

Note that that means the caller will still see any changes you make to the set, if they hang on to the returned set. If you don't want that, you'll have to make a copy... there's no (simple) way round that. (In theory you could make an unmodifiable copy and return a reference to that same copy until the next time you make a change, but that gets messy.)

One important point is to document whatever you choose so that the caller doesn't get any nasty surprises. In many ways I think that's actually the most important thing in most applications, where the caller isn't actually malicious. So long as it's clear what the effects will be, being defensive isn't quite as important in most cases. Of course, if your caller could be some untrustworthy code and your set is vital to security etc, you're in a different situation.

海风掠过北极光 2024-08-12 20:53:17

这取决于上下文。有几个选项:

  1. 返回集合。调用者可以根据需要修改集合,但不能分配新的集合。这是最便宜的,但提供的保护最少。
  2. 返回一个视图(使用Collections类的unmodifyingXXX工厂)。调用者无法修改集合,但集合的更新对调用者来说是可见的。这通常相对便宜,因为没有分配元素存储;仅创建一个包装器。
  3. 使用适当集合的复制构造函数返回集合的快照。在这里,调用者获得该集合的副本。他们可以修改副本,但原始副本不会更新,并且对原始副本的更新在副本中不可见。这是最贵的。

It depends on the context. There are a few options:

  1. Return the collection. The caller can modify the collection as they wish, but they cannot assign a new collection. This is cheapest, but offers the least protection.
  2. Return a view (using unmodifiableXXX factories of the Collections class). The caller cannot modify the collection, but updates to the collection will be visible to the caller. This is usually relatively cheap because element storage isn't allocated; only a wrapper is created.
  3. Return a snapshot of the collection, using a copy constructor of the appropriate collection. Here, the caller gets a copy of the collection. They can modify the copy, but the original is not updated, and updates to the original are not visible in the copy. This is the most expensive.
絕版丫頭 2024-08-12 20:53:17

您需要有多“安全”?您返回的数组引用了您的集合所保存的相同对象,如果这些对象具有设置器,那么您就允许调用者修改您的集合的内容...可以吗?

可以克隆该集合,或者提供用于访问该集合的不可变接口。这是一个判断。如果我正在开发框架代码,我倾向于在安全和克隆方面犯错误。当客户端耦合更紧密时(例如同一包中的相关类),我倾向于不那么保守。

How "safe" do you need to be? The array you return has references to the same objects that your set holds, if those objects have setters then you've allowed the caller to modify the contents of your set ... is that OK?

It would be possible to clone the set, or provide an immutable interface for accessing the set. It's a judgement call. If I were developing framework code I would tend to err on the side of safety and clone. I tend to be less conservative when the client is more tightly coupled, a related class in the same package, say.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文