1

首先,这是Java 7 的 Collections.checked*添加的行为:

由于 null 被认为是任何引用类型的值,因此返回的集合允许在支持集合时插入 null 元素。

不过,这似乎没有在兼容性文档中列出。演示:

public class MyAPI {
    private Set<Polygon> polygons = new Collections.checkedSet(new HashSet<Polygon>(), Polygon.class);
    public Set<Polygon> getPolygons() {
        return polygons;
    }
}
public class MyAPITest {
    // This JUnit test passes when using Java 6 or earlier, but fails for Java 7.
    @Test(expected=NullPointerException.class)
    public void testAddNullPolygon() {
        new MyAPI().getPolygons().add(null);
    }
}

如您所见,我正在编写一个 API,它公开了一个Set供客户端代码填充的 API。根据我的阅读,这是Collections.checkedCollectionetc 的用例之一:添加的运行时检查有助于防止插入奇怪的东西。

我已经更改了我的 API 以处理空值,但我担心客户端代码有时会抛出 NPE,有时不会,这取决于最终用户运行的 Java 版本。就是感觉破了。理想情况下,我想保留旧行为,防止在插入时出现空值。

我想我的选择是:

  1. 完全放弃运行时检查。

  2. 不用担心,相信客户端代码永远不会插入空值。

  3. 声明我的 API 只支持 JRE 7。

  4. 使用 Guava,它看起来很棒,但它是我的 API 会背负的额外依赖项。

  5. 滚动我自己的 Set 包装器,它强制执行 null 和类型检查。

  6. 我缺少的其他一些更优雅的解决方案。

任何指导将不胜感激!

4

1 回答 1

1

我最终选择的选项是去掉checkedCollection,使暴露的集合不可修改。API 的用户需要调用额外的 add/remove/clear 方法之一来修改集合。

这在一定程度上限制了 API 用户:例如,他们不能使用 addAll 在单个方法调用中复制另一个集合的所有元素。但这是简单性和类型安全性的公平权衡。

public class MyAPI {
    private Set<Polygon> polygons = new HashSet<Polygon>();
    private Set<Polygon> polygonsReadonlyView = Collections.unmodifiableSet(polygons);
    public Set<Polygon> getPolygons() {
        return polygonsReadonlyView;
    }
    public boolean addPolygon(Polygon p) {
        if (p == null) {
            throw new IllegalArgumentException("polygon cannot be null");
        }
        return polygons.add(p);
    }
    public boolean removePolygon(Polygon p) {
        return polygons.remove(p);
    }
    public void clearPolygons() {
        polygons.clear();
    }
}

所有这一切的一个收获是永远不要依赖 checkCollection 来防止插入空值。

谷歌搜索可能会建议这一点......但不要这样做!空值检查不是checkedCollection 的用例之一。

于 2012-12-03T13:13:14.470 回答