例如,如果我有一个像
这样的界面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
。我觉得期望仍然是返回的集合允许修改,除非另有说明。如果我想返回一个不可变集合,我会将返回类型指定为不可变类,如果可能的话。
我想我的问题是:通常的假设(根据我的经验)是返回的集合是可变的,实现返回常规集合并返回不可变集合的接口方法是否合理?
答案 0 :(得分:9)
我不知道接口的精神是什么,但Java集合接口的规范(又名Javadoc ;-)清楚地说明了不可变集合可能当用户尝试修改它们时抛出UnsupportedOperationException
。
编辑以回答您编辑过的问题
首先,我同意应该记录返回的集合是否可变。但是,我不同意默认的假设是该集合是可变的。此外,当返回的集合是可变的时,我希望这是记录的,并且还记录了当我修改集合时会发生什么(特别是:当我修改集合时,集合来自哪个对象,或者它只是一些数据的副本)。
使用ImmutableSet
,ImmutableList
等类型可能会很好,但Java标准库没有它们。原因是情况不是布尔值:集合可以是部分可修改的,例如因为它允许删除元素但不允许添加新元素。为所有可能的组合设置单独的接口并不是一个好主意,因此Java设计者决定不使用任何接口。
您可以使用外部库,例如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的作者想要将不可变集合无缝集成到它中,他们不得不做出一些妥协。