集合篇 —— Collection(1):JDK 中的重复实现问题
1. 问题的提出
在 Java 的集合体系当中,无论是 List(列表)还是 Set(集),在设计的时候都存在一个很奇怪的现象:这两种集合的接口,Java 都为其设计了抽象类 AbstractList 和 AbstractMap,这是模板模式的一种典型实现,在抽象模板中,提供了一些这些集合各自的公共行为的实现。
然而,在这两种集合的典型实现类当中,出现了这种继承和实现的结构:
- ArrayList:

- LinkedList:

- Vector:

- HashSet:

- TreeSet:

这些实现类都有一个共同的特征,他们即继承了抽象父类,也实现了集合接口。这两个抽象父类(或其抽象子类),AbstractList、AbstractSet 都各自实现了 List、Set 接口。
如此一来,实现这些接口就会显得非常多余,因为这些实现类,无论接口的与否,对于功能并不影响。甚至于,从设计的角度来说,这些接口的实现也显得十分多余。我将这种行为称之“接口的重复实现”(类似的设计同样出现在了 Map 结构中)。
以上的情况,并不是因为版本的更迭,为了提供向下兼容二产生的。通过 Javadoc 可以发现,这些设计都是自 JDK1.2 以来延续至今。
2. 为什么实现接口是多余的?
关于接口和抽象类,这是一个老生常谈的问题,在这里不想多加赘述。但是我在编码时奉行一个原则:只要不是使用模板模式,接口的优先级是高于抽象类的。这句话反过来理解:如果使用了模板模式,那么抽象类的优先级应该是高于接口实现的。
以 List 为例,现在我需要新编写一个列表结构的类(不是抽象类),放在我面前有两个选择:AbstractList 和 List。我有以下三种设计方式:
- 实现 List 接口:我必须重写所有 List 接口中定义的方法,无论这些方法是否是我所必须的。这个工作可能会比较繁琐,但优点在于,我不会遗漏任何一个方法,因为这会报出编译错误。
- 继承 AbstractList 抽象类:OK,一些公共的行为已经定义了,我会被强制定义所有抽象父类中的声明为 abstract 的方法。但是抽象父类中的一些行为,并不是我所希望的我要重新定义,或者说,我有一些基于自身特性的,更优的解决方案。这种情况并不少见:AbstractList 中实现了基于迭代器 Iterator 的 indexOf() 方法,使用一个指针跟进当前的位置。但是在 ArrayList 中,因为这是基于数组的集合,我可以通过数组的下标直接获取集合的值。但是,这些重新定义的方法需要人为寻找,编译器并不会做出提示。如果只是 indexOf() 之类的方法,那只是性能方面的问题,如果没有重写 remove() 方法,那问题就大了。AbstractList 的 remove() 方法是这么定义的:

- 继承 AbstractList 抽象类的同时,实现 List 接口:这种行为有什么好处吗?再一次的实现 List 接口,能改进2中的弊端吗?或者说,这么做有什么特殊的意义吗?至少从设计的角度来说,我暂时没有找到这么做的优势。
3. 一个勉强的解释
对于这种“接口的重复定义”现象,我有一个比较勉强的解释。我们常说,接口的作用在于规范一种行为,那么是不是可以这么说,重复定义接口的目的,在于明确行为!
如果说这么一种解释过于宽泛的话,从编码的角度可以这么说:将间接实现接口变为直接实现。如此一来,在 Javadoc 形成的文档中,我可以直观地知道这是一个列表,而不是通过 进入 AbstractList 的 Javadoc,才明确这一点。
另外,考虑一种极端的情况:如果哪一天需要推翻 AbstractList 这个模板,重复实现可以减低改动代码的风险——当然,这种情况出现的概率很低,如果出现这种情况,最初的设计人员绝对是要被狠狠批判的。(如果真的需要进行这种颠覆性的修改,修改抽象类的代价一定是小于修改接口的,所以被修改的一定是抽象类)
这种情况在代码中的体现是:在反射抓取接口时,最初只会抓取直接实现的接口,如果想要获得间接实现的接口,需要做更深一步的处理。如下图所示:

4. 以后该怎么做?
既然 JDK 中给出了这种明确的做法,是不是可以考虑,以后在使用模板模式编码时,将抽象父类所实现的接口,实现类再重新再实现一次?我觉得这个想法是可以好好考虑一下的,在特定的场景下可以这么做,并不强制。原因很简单:在 Java 源码中,单独继承抽象类,或者单独实现接口的情况非常多。
集合篇 —— Collection(1):JDK 中的重复实现问题的更多相关文章
- Java集合篇六:Map中key值不可重复的测试
package com.test.collection; import java.util.HashMap; import java.util.Map; //Map中key值不可重复的测试 publi ...
- python删除列表中得重复得数据
解决思想:将列表转换为 集合,利用集合删除重复数据得特性删除重复数据,然后将集合转换为列表 #删除列表中得重复元素 def delect_1 (lt): s = set(lt) lt = list(s ...
- 关于JDK中的集合总结(二)
1.2版本的JDK才出现的java集合框架. 下面介绍说一下Vector的一些特点. import java.util.Enumeration; import java.util.Iterator; ...
- Java中的集合框架-Collection(二)
上一篇<Java中的集合框架-Collection(一)>把Java集合框架中的Collection与List及其常用实现类的功能大致记录了一下,本篇接着记录Collection的另一个子 ...
- 关于JDK中的集合总结(三)
泛型: jdk1.5出现的安全机制. 好处: 1,将运行时期的问题ClassCastException转到了编译时期. 2,避免了强制转换的麻烦. <>:什么时候用?当操作的引用数据类型不 ...
- 关于JDK中的集合总结(一)
静态方法只能继承,不能重写(Override). StringBufffer,数组,对象 都是容器. 加入数据,“zhangsan” true ,67, 三个数据数据类型不同,不能用数组作为集合,只能 ...
- Java 去除 ArrayList 集合中的重复元素
// One practice package Collection; import java.util.ArrayList; import java.util.Iterator; // 去除 Arr ...
- Java中Set集合是如何实现添加元素保证不重复的?
Java中Set集合是如何实现添加元素保证不重复的? Set集合是一个无序的不可以重复的集合.今天来看一下为什么不可以重复. Set是一个接口,最常用的实现类就是HashSet,今天我们就拿HashS ...
- 牛客网Java刷题知识点之Java 集合框架的构成、集合框架中的迭代器Iterator、集合框架中的集合接口Collection(List和Set)、集合框架中的Map集合
不多说,直接上干货! 集合框架中包含了大量集合接口.这些接口的实现类和操作它们的算法. 集合容器因为内部的数据结构不同,有多种具体容器. 不断的向上抽取,就形成了集合框架. Map是一次添加一对元素. ...
随机推荐
- Codeforces Round #411 div2
A. Fake NP 题意:询问一个区间[L,R]出现次数最多的正整数因子(>1). 一个区间内一个因子P出现次数大概为[R/P]-[(L-1)/P],约等于(R-L+1)/P,P取2时最优.注 ...
- CoreData介绍
http://blog.csdn.net/zh952016281/article/details/52105683 写在前面 在CoreData中有一些常用的类,称呼可能各不相同.所以这里先约定一些关 ...
- 使用EventLog组件读写事件日志
实现效果: 知识运用: Eventlog类的SourceExists方法 //确定指定的事件源是否已在本地计算机注册 public static bool SourceExists(string s ...
- Connectivity
6492: Connectivity 时间限制: 1 Sec 内存限制: 128 MB提交: 118 解决: 28[提交][状态][讨论版][命题人:admin] 题目描述 There are N ...
- 2_分布式计算框架MapReduce
一.mr介绍 1.MapReduce设计理念是移动计算而不是移动数据,就是把分析计算的程序,分别拷贝一份到不同的机器上,而不是移动数据. 2.计算框架有很多,不是谁替换谁的问题,是谁更适合的问题.mr ...
- Java中如果把构造方法也私有化,如何创建对象?Java的单例设计模式——饿汉式和懒汉式区别
Java的单例模式——饿汉式 package com.swift; //Java单例设计模式——恶汉式 public class SinglePerson { private String name= ...
- Java 吃金币游戏设计与制作,下载版后补,代码没问题
package com.swift; import java.awt.Color; import java.awt.Point; import java.awt.event.KeyEvent; imp ...
- 利用SignalR实现实时聊天
2018/10/10:博主第一次写原创博文而且还是关于C#的(博主是从前端转过来的),菜鸟一枚,如果有什么写的不对,理解错误,还望各位轻喷.,从SignalR开始! 首先先介绍一下关于SignalR的 ...
- python中的sort、sorted排序
我们通常会遇到对数据库中的数据进行排序的问题,今天学习一下对列表和字典的排序方法. 列表 第一种:内建方法sort sort()对列表排序是永久性的排序. 用法:sort(*, key=None, r ...
- 201621123080《Java程序设计》第12周学习总结
201621123080<Java程序设计>第12周学习总结 1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多流与文件相关内容. 2. 面向系统综合设计-图书馆管理系 ...