Home Articles

Guava Immutable *类是否满足它们实现的标准集合接口?

Asked
Viewed 1287 times
2

例如,如果我有一个类似的界面

public interface Partition<E> {
    Set<E> getIncluded();
    Set<E> getExcluded();
}

我这样实现它

public class ImmutablePartition<E> implements Partition<E> {
    private final ImmutableSet<E> included;
    private final ImmutableSet<E> excluded;

    ImmutablePartition(Set<E> included, Set<E> excluded) {
        this.included = ImmutableSet.copyOf(included);
        this.excluded = ImmutableSet.copyOf(excluded);
    }

    @Override 
    public Set<E> getIncluded() {
        return included;
    }

    @Override
    public Set<E> getExcluded() {
        return excluded;
    }
}

我真的实现了原始界面的精神吗?如果客户端代码返回 Set<E> ,尝试操作它,并获得一个 UnsupportedOperationException ,肯定会首先违背实现 Set<E> 接口的目的?

此问题适用于实现 java.util 标准集合接口的所有Guava Immutable *集合 .

edit: 正如我在下面提醒的那样, Collection 接口为不支持的变异方法指定了 UnsupportedOperationException . 除非另有说明,我觉得期望仍然是返回的集合将允许修改 . 如果我想返回一个不可变集合,我会将返回类型指定为不可变类,如果可能的话 .

我想我的问题是:通常的假设(根据我的经验)是返回的集合是可变的,实现一个返回一般集合并返回一个不可变集合的接口方法是否合理?

5 Answers

  • 9

    使用ImmutableSet作为返回类型是完全有效的(在我看来,更好) . 人类读者的线索是,他们所得到的将是不可改变的 .

  • 5

    我不知道接口的精神是什么,但Java集合接口的规范(a.k.a.Javadoc ;-)明确指出当用户尝试修改它们时,不可变集合可能会抛出 UnsupportedOperationException .

    Edit to also answer your edited question

    首先,我同意应该记录返回的集合是否可变 . 但是,我不同意默认的假设是该集合是可变的 . 此外,当返回的集合是可变的时,我希望这是记录的,并且还记录了当我修改集合时会发生什么(特别是:当我修改集合时,集合来自哪个对象,或者它只是一些数据的副本) .

    ImmutableSetImmutableList 等类型可能会很好,但Java标准库没有它们 . 原因是情况不是布尔值:集合可以是部分可修改的,例如因为它允许删除元素但不允许添加新元素 . 为所有可能的组合设置单独的接口并不是一个好主意,因此Java设计者决定不使用任何接口 .

    您可以使用外部库,例如Guava,但这也有缺点:

    • 您将依赖项引入外部库

    • Guava不可变集合包含原始数据的副本,而不是视图 . 如果你的 class 想要公开一些内部列表而没有调用者更改内部数据的可能性,那么每次复制都可能不是你想要的 .

  • 1

    Do the Guava Immutable* classes satisfy the standard collection interfaces they implement?

    YES .

    接口方法调用的行为是特定于实现的,直到文档明确限制 . 投掷 UnsupportedOperationException 不违反接口 Contract .

    示例:

    java.util.Collections.UnmodifiableList ,执行 java.util.List 执行以下操作:

    public void remove() {
        throw new UnsupportedOperationException();
    }
    public void set(E e) {
        throw new UnsupportedOperationException();
    }
    public void add(E e) {
        throw new UnsupportedOperationException();
    }
    

    如果检查 List 接口的特定方法的javadoc,例如, boolean add(E e) ,您可以找到以下内容:

    如果添加操作,则抛出UnsupportedOperationException
    此列表不支持

  • 1

    Arrays.asList() 已经返回不允许 add() 的列表 .

    所以我认为返回不可修改或部分可修改的集合是可以的,判断Java精神是由Java标准库规范定义的 .

  • 0

    在我看来,它没有 . 我知道文档声明有可选操作,甚至这些方法都可能抛出 UnsupportedOperationException (这是一个未经检查的方法,因此调用者可能没有准备好捕获它),但我认为接口的目的是指定什么可以完成一个实现,并且由于这些方法存在于接口上,因此应该正确实现它们 . 如果有可选操作,则应该有一些API通过代码发现它,而不是通过阅读文档 .

    我甚至认为抛出 UnsupportedOperationException 违反了Liskov substitution principle . 毫无疑问,我不批评Guava,这是标准Java集合框架的一个问题,Guava的作者希望将不可变集合无缝集成到它中,并且他们必须做出一些妥协 .

Related