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

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

例如,如果我有一个像

这样的界面
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> 接口的目的吗?

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

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

我想我的问题是:通常假设(根据我的经验)returned 集合将是可变的,实现一个 returns 的接口方法是否明智通用集合和 return 不可变集合?

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

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

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

编辑以同时回答您编辑的问题

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

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

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

  • 您引入了对外部库的依赖
  • Guava 不可变集合包含原始数据的副本,而不是视图。如果您的 class 只是想制作一些内部列表 public 而调用者不可能更改内部数据,那么每次制作副本可能不是您想要的。

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

使用 ImmutableSet 作为 return 类型是完全有效的(在我看来,更可取)。人类读者的线索是他们得到的东西将是不可变的。

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

我什至认为抛出 UnsupportedOperationException 违反了 Liskov substitution principle。别搞错了,我不是在这里批评 Guava,这是标准 Java 集合框架的问题,Guava 的作者想要将不可变集合无缝地集成到其中,他们不得不做出一些妥协。