AbstractCollection
实现了 Collection
接口。那么为什么会有 AbstractCollection
并且我们为什么使用 Collection
而不是直接使用 AbstractCollection
呢?
AbstractCollection
实现了 Collection
接口。那么为什么会有 AbstractCollection
并且我们为什么使用 Collection
而不是直接使用 AbstractCollection
呢?
也许回答有点晚,但以防万一我想回答这个问题。
AbstractCollection
被使用是因为所有的子集合都共享一些功能,就像Krzysztof所说的那样,由于这个功能是通用的(完全相同的代码行),我们可以使用继承来重用该代码并使设计更清晰、易于理解和更易于维护。
我们使用接口,因为接口定义了每个实现接口的类必须提供的“契约”,即 子类必须具有的方法。这个问题与为什么不能使用 ArrayList
而使用 List 类似,你可以这样做,但90%的时间你会对添加/设置、删除和知道 ArrayList
的大小感兴趣,而 List
接口确保你在 new 后编写的任何东西都将实现这些方法(和其他方法),因此你可以调用它们,并且多态性将起作用。此外,使用接口作为引用允许你更改具体实现的类型而不更改任何内容(仅更改 new 调用),还允许你在运行时更改具体类型,如果使用特定类的引用,则将自己限制在该引用和仅该引用上。
补充一下@Angelixus在https://dev59.com/XYTba4cB1Zd3GeqP2jBW#58513874中所说的:
不要这样做的一个原因是:
AbstractCollection col = (AbstractCollection) someCollection;
子类型的Collection
并不保证扩展AbstractCollection
基类。
当然,大多数(如果不是全部)标准集合类型确实扩展了这个基类。然而,这可能不适用于所有第三方集合类。如果你遇到一个没有扩展AbstractCollection
的集合类,上述代码将根据someCollection
的声明类型产生编译错误或运行时异常。
Collection
接口声明了所有实现类必须提供的有用方法。但是,如果每个实现 Collection
接口的类都必须提供这么多方法,那就很麻烦。
为了让我们的生活更轻松,库提供了一个类 AbstractCollection
,它将基本方法 size
和 iterator
声明为抽象的,但通过这些方法来实现例行程序方法。例如:
public abstract class AbstractCollection<E>
implements Collection<E>
{
...
public abstract Iterator<E> iterator();
public boolean contains(Object obj)
{
for (E element : this) // calls iterator()
if (element.equals(obj))
return true;
return false;
}
...
}
现在具体的集合类可以扩展AbstractCollection
类。现在由我们来提供iterator
方法,但是contains
方法已经由AbstractCollection
超类处理。然而,如果子类有更有效实现contains
的方法,可以自由地这样做。但是这种方法有点过时了。
这些类被称为框架实现类。命名规则是AbstractInterface,其中interface是接口的名称(例如-AbstractList)。它们是通过组合接口和抽象类创建的。
通常情况下,当我们设计一个接口并且接口方法有显然的实现时,我们可以在接口中提供默认方法,这样实现接口的人可以遵循默认实现,并在其上提供自定义实现。但是,在默认方法中提供的实现协助有限制。原因是接口不允许包含实例字段或非公共静态成员。但是,我们可以结合接口和抽象类的优点。
请参考下面的示例。假设我们有一个方法,该方法接受int数组并返回一个包含每个元素的平方的Integers列表。
public static List<Integer> squaredList(int [] e) {
return new AbstractList<Integer>() {
@Override
public Integer get(int index) {
return e[index] * e[index];
}
@Override
public int size() {
return e.length;
}
};
}
AbstractCollection,有2个方法是抽象的。如果您想要实现一个不可修改的集合,程序员只需要扩展此类并为迭代器和大小方法提供实现即可。(由iterator方法返回的迭代器必须实现hasNext和next。) 这种类型的实现在您实现具有返回Collection方法的不可变类时非常有用。
要实现可修改的集合,程序员还必须覆盖此类的add方法(否则会抛出UnsupportedOperationException),并且由iterator方法返回的迭代器还必须实现其remove方法。
有关详细信息,请参阅Java文档 https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/AbstractCollection.html
代码是最好的文档 :-) 可以在 JDK 中检查源代码,例如在 code-grep pages 中。
在那里,您可以看到,在 java 6b-14 中,AbstractCollection
类提供了 isEmpty()
、contains()
、toArray()
、toArray(T[] a)
、remove(Object o)
、containsAll(Collection<?> c)
、addAll(Collection<? extends E> c)
、removeAll(Collection<?> c)
、retainAll(Collection<?> c)
、clear()
和 toString()
的直接实现。
上述方法取决于 add(E e)
、iterator()
和(可选)remove(Object o)
的实现。
以下是一段值得引用的有趣语录,来自Java核心技术 卷I:基础篇, 第10版:
在Java API中,你会发现许多接口都有相应的实现类,例如
Collection
/AbstractCollection
或者MouseListener
/MouseAdapter
。但在Java SE 8中,这种技术已经过时了。只需在接口中实现方法即可。
Collection
。 - Sotirios DelimanolisAbstractCollection
,因为它们将提供自己的在Collection
接口中定义的所有方法的实现。例如,Guava的ForwardingList
。 - biziclop