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

时间:2016-02-24 10:21:27

标签: java collections interface guava immutability

例如,如果我有一个像

这样的界面
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 *集合。

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

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

5 个答案:

答案 0 :(得分:9)

我不知道接口的精神是什么,但Java集合接口的规范(又名Javadoc ;-)清楚地说明了不可变集合可能当用户尝试修改它们时抛出UnsupportedOperationException

编辑以回答您编辑过的问题

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

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

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

  • 您将依赖项引入外部库
  • Guava不可变集合包含原始数据的副本,而不是视图。如果你的班级只想公开一些内部列表,而调用者没有更改内部数据的可能性,那么每次复制都可能不是你想要的。

答案 1 :(得分:5)

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

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

示例:

实现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),您可以找到以下内容:

  throws UnsupportedOperationException if the add operation
          is not supported by this list

答案 2 :(得分:1)

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

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

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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

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