为什么Java中的许多Collection类都扩展抽象类并实现接口?
为什么Java中的许多Collection类扩展了Abstract类并实现了接口(也由给定的抽象类实现)?
例如,类 HashSet
扩展了 AbstractSet
并实现了 Set
,但 AbstractSet
已经实现了 Set
代码>.
Why do many Collection classes in Java extend the Abstract class and also implement the interface (which is also implemented by the given abstract class)?
For example, class HashSet
extends AbstractSet
and also implements Set
, but AbstractSet
already implements Set
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
这是记住该类真正实现该接口的一种方式。
它不会产生任何不良影响,并且可以帮助理解代码,而无需遍历给定类的完整层次结构。
It's a way to remember that this class really implements that interface.
It won't have any bad effect and it can help to understand the code without going through the complete hierarchy of the given class.
从类型系统的角度来看,如果类没有再次实现接口,它们不会有任何不同,因为抽象基类已经实现了它们。
这是真的。
他们无论如何实现它的原因(可能)主要是文档:
HashSet
is-aSet
。通过在末尾添加implements Set
可以明确这一点,尽管这并不是绝对必要的。请注意,使用反射实际上可以观察到差异,但如果
HashSet
不直接实现Set
,我将很难生成一些会中断的代码。From the perspective of the type system the classes wouldn't be any different if they didn't implement the interface again, since the abstract base classes already implement them.
That much is true.
The reason they do implement it anyways is (probably) mostly documentation: a
HashSet
is-aSet
. And that is made explicit by addingimplements Set
to the end, although it's not strictly necessary.Note that the difference is actually observable using reflection, but I'd be hard-pressed to produce some code that would break if
HashSet
didn't implementSet
directly.这在实践中可能并不重要,但我想澄清一下,显式实现接口与通过继承实现接口完全并不相同。差异存在于已编译的类文件中,并且通过反射可见。例如,
输出仅显示 显式 由
ArrayList
实现的接口,按照它们在源代码中编写的顺序,[在我的 Java 版本上] 是:输出不包括由超类实现的接口,或者是所包含接口的超接口。特别是,上面缺少
Iterable
和Collection
,即使ArrayList
隐式实现了它们。要找到它们,您必须递归地迭代类层次结构。如果某些代码使用反射并依赖于显式实现的接口,那将是不幸的,但这是可能的,因此集合库的维护者现在可能不愿意更改它,即使他们想这样做。 (有一个名为 Hyrum 定律 的观察结果:“只要有足够数量的 API 用户,这并不重要您在合同中承诺的内容;您的系统的所有可观察行为都将取决于某人”。)
幸运的是,这种差异不会影响类型系统。表达式
new ArrayList<>() instanceof Iterable
和Iterable.class.isAssignableFrom(ArrayList.class)
的计算结果仍然为true
。This may not matter much in practice, but I wanted to clarify that explicitly implementing an interface is not exactly the same as implementing it by inheritance. The difference is present in compiled class files and visible via reflection. E.g.,
The output shows only the interfaces explicitly implemented by
ArrayList
, in the order they were written in the source, which [on my Java version] is:The output does not include interfaces implemented by superclasses, or interfaces that are superinterfaces of those which are included. In particular,
Iterable
andCollection
are missing from the above, even thoughArrayList
implements them implicitly. To find them you have to recursively iterate the class hierarchy.It would be unfortunate if some code out there uses reflection and depends on interfaces being explicitly implemented, but it is possible, so the maintainers of the collections library may be reluctant to change it now, even if they wanted to. (There is an observation termed Hyrum's Law: "With a sufficient number of users of an API, it does not matter what you promise in the contract; all observable behaviors of your system will be depended on by somebody".)
Fortunately this difference does not affect the type system. The expressions
new ArrayList<>() instanceof Iterable
andIterable.class.isAssignableFrom(ArrayList.class)
still evaluate totrue
.与科林·赫伯特不同,我不相信那些关心可读性的人。 (每个认为标准 Java 库是由无可挑剔的上帝编写的人,应该看看它们的来源。我第一次这样做时,我对代码格式和大量复制粘贴的块感到震惊。)
我敢打赌,已经晚了,他们累了,而且无论如何都不在乎。
Unlike Colin Hebert, I don't buy that people who were writing that cared about readability. (Everyone who thinks standard Java libraries were written by impeccable gods, should take look it their sources. First time I did this I was horrified by code formatting and numerous copy-pasted blocks.)
My bet is it was late, they were tired and didn't care either way.
来自 Joshua Bloch 的《Effective Java》:
您可以通过添加一个抽象骨架实现类来配合接口,从而结合接口和抽象类的优点。
接口定义类型,也许提供一些默认方法,而骨架类在原始接口方法之上实现其余的非原始接口方法。扩展骨架实现省去了实现接口的大部分工作。这是模板方法模式。
按照惯例,骨架实现类称为
AbstractInterface
,其中Interface
是它们实现的接口的名称。例如:From the "Effective Java" by Joshua Bloch:
You can combine the advantages of interfaces and abstract classes by adding an abstract skeletal implementation class to go with an interface.
The interface defines the type, perhaps providing some default methods, while the skeletal class implements the remaining non-primitive interface methods atop the primitive interface methods. Extending a skeletal implementation takes most of the work out of implementing an interface. This is the Template Method pattern.
By convention, skeletal implementation classes are called
AbstractInterface
whereInterface
is the name of the interface they implement. For example:我也相信这是为了清晰起见。 Java 集合框架具有相当多的接口层次结构,定义了不同类型的集合。它从 Collection 接口开始,然后通过三个主要子接口 Set、List 和 Queue 进行扩展。还有继承Set的SortedSet和继承Queue的BlockingQueue。
现在,如果实现它们的具体类明确声明它正在实现层次结构中的哪个接口,那么它们就更容易理解,即使它有时看起来很多余。正如您所提到的,像 HashSet 这样的类实现了 Set,但是像 TreeSet 这样的类虽然也扩展了 AbstractSet,但实现了 SortedSet,而后者比 Set 更具体。 HashSet 可能看起来多余,但 TreeSet 则不然,因为它需要实现 SortedSet。尽管如此,这两个类都是具体的实现,如果它们在声明中都遵循一定的约定,就会更容易理解。
甚至有一些类实现了不止一种集合类型,例如 LinkedList,它同时实现了 List 和 Queue。然而,至少有一类有点“非常规”,即 PriorityQueue。它扩展了 AbstractQueue 但没有显式实现 Queue。别问我为什么。 :)
(参考来自Java 5 API)
I also believe it is for clarity. The Java Collections framework has quite a hierarchy of interfaces that defines the different types of collection. It starts with the Collection interface then extended by three main subinterfaces Set, List and Queue. There is also SortedSet extending Set and BlockingQueue extending Queue.
Now, concrete classes implementing them is more understandable if they explicitly state which interface in the heirarchy it is implementing even though it may look redundant at times. As you mentioned, a class like HashSet implements Set but a class like TreeSet though it also extends AbstractSet implements SortedSet instead which is more specific than just Set. HashSet may look redundant but TreeSet is not because it requires to implement SortedSet. Still, both classes are concrete implementations and would be more understandable if both follow certain convention in their declaration.
There are even classes that implement more than one collection type like LinkedList which implements both List and Queue. However, there is one class at least that is a bit 'unconventional', the PriorityQueue. It extends AbstractQueue but doesn't explicitly implement Queue. Don't ask me why. :)
(reference is from Java 5 API)
回答太晚了?
我正在猜测以验证我的答案。假设以下代码
HashMap扩展了AbstractMap
(不实现Map)AbstractMap实现了Map
现在想象一下一些随机的人来了,将Map的实现更改为具有完全相同集合的某些java.util.Map1方法作为 Map
在这种情况下,不会有任何编译错误,jdk 会被编译(当然测试会失败并捕获这个)。
现在,任何使用 HashMap 作为 Map m= new HashMap() 的客户端都将开始失败。这是下游很多。
由于 AbstractMap、Map 等都来自相同的产品,因此这个论点显得幼稚(很可能是,也可能不是。),但是想想一个基类来自不同 jar/第三方库等的项目。然后第三方/不同的团队可以改变他们的基本实现。
通过在 Child 类中实现“接口”,开发人员也尝试使该类自给自足,防止 API 损坏。
Too late for answer?
I am taking a guess to validate my answer. Assume following code
HashMap extends AbstractMap
(does not implement Map)AbstractMap implements Map
Now Imagine some random guy came, Changed implements Map to some java.util.Map1 with exactly same set of methods as Map
In this situation there won't be any compilation error and jdk gets compiled (off course test will fail and catch this).
Now any client using HashMap as Map m= new HashMap() will start failing. This is much downstream.
Since both AbstractMap, Map etc comes from same product, hence this argument appears childish (which in all probability is. or may be not.), but think of a project where base class comes from a different jar/third party library etc. Then third party/different team can change their base implementation.
By implementing the "interface" in the Child class, as well, developer's try to make the class self sufficient, API breakage proof.
在我看来,当一个类实现一个接口时,它必须实现其中存在的所有方法(因为默认情况下它们是接口中的公共和抽象方法)。
如果我们不想实现接口的所有方法,那么它必须是一个抽象类。
因此,如果某些方法已经在实现特定接口的抽象类中实现,并且我们必须为其他未实现的方法扩展功能,我们将需要在我们的类中再次实现原始接口以获得剩余的方法集。维护接口制定的合同规则。
如果仅实现接口并再次使用类中的方法定义覆盖所有方法,则会导致返工。
In my view,when a class implements an interface it has to implement all methods present in it(as by default they are public and abstract methods in an interface).
If we don't want to implement all methods of interface,it must be an abstract class.
So here if some methods are already implemented in some abstract class implementing particular interface and we have to extend functionality for other methods that have been unimplemented,we will need to implement original interface in our class again to get those remaining set of methods.It help in maintaining the contractual rules laid down by an interface.
It will result in rework if were to implement only interface and again overriding all methods with method definitions in our class.
我想可能有一种不同的方式来处理集合、接口的成员,即使提供默认操作实现并不作为万能的。循环队列与后进先出队列可能都实现相同的接口,但它们的具体操作会实现不同,对吗?
I suppose there might be a different way to handle members of the set, the interface, even when supplying the default operation implementation does not serve as a one-size-fits-all. A circular Queue vs. LIFO Queue might both implement the same interface, but their specific operations will be implemented differently, right?
如果你只有一个抽象类,你就不能创建一个自己的类,它也从另一个类继承。
If you only had an abstract class you couldn't make a class of your own which inherits from another class too.