Java - 接口还是抽象类
Java有两种机制可以为某个抽象提供多种实现——Interface和abstract class。
Interface 和 abstract class,
除了比较明显的区别(也就是能否提供基本实现),
比较重要的区别是——
接口的实现类可以处于类层次的任何一个位置,而抽象类的子类则受到这一限制。
Existing classes can be easily retrofitted to implement a new interface.
即,如果一个类要实现某个接口,只需要加上implements语句和方法实现。
而继承一个抽象类则可能会破坏类层次,比如,属于两个不同类层次的类都想要某一个抽象类的行为时我们需要重新整理一下类层次。
Interfaces are ideal for defining mixins.
(“mixin”这一词不知该如何翻译,翻译为"混合类型"显得很僵硬。)
个人觉得这一条和第一条几乎是说明同一个问题。
关于mixin,作者用Comparable举例,其实现类表明自己的实例有相互比较的能力。
而抽象类不能随意更新到现有的类中,考虑到类层次结构,为类提供某个行为的抽象时接口更为合适。
Interfaces allow the construction of nonhierarchical type frameworks.
事实上类层次结构并不是一无是处的,但这需要我们思考:是否需要组织为严格的层次结构。
比如,歌手和作曲家是否需要层次结构? 显然他们没有层次关系。
即:
public interface Singer{
AudioClip sing(Song s);
}
public interface Songwriter{
Song compose(boolean hit);
}
如果是创作型歌手,他需要同时扩展Singer和Songwriter。
幸好上面二者都是interface,于是我们可以:
public interface SingerSongwirter extends Singer, Songwriter{
AudioClip strum();
void actSensitive();
}
如果试图使用抽象类解决这一问题,
也许我们可以将一个类的对象作为另一个类的field,
也许我们也可以将它们组织成类层次关系。
但如果类的数量越来越多,出现更多的组合,结构变得越来越臃肿(ps:称为"combinatorial explosion")。
另外,说说接口比较明显的"缺点",也就是不能提供任何实现。
但需要注意的是,这个特征并不能使抽象类取代接口。
比较好的方法将两者结合起来,这种用法很常见。
比如Apache Shiro的DefaultSecurityManager的类层次(当然,Shiro还在不断完善中...):

即,为接口中的定义提供一个抽象的骨架实现(skeletal implementation),将接口和抽象类的优点结合起来。
通常,一个抽象类为接口提供skeletal实现时存在这样的命名规则,比如AbstractSet和Set、AbstractCollection和Collection。
如果我用这个skeletal实现,岂不是又要受类层次的困扰?
确实是这样,但skeletal的意义并不在于灵活性。
先举书中的代码例子,静态工厂方法使用skeletal类返回整型列表(ps:过度使用自动装拆箱...):
public class IntArrays {
static List<Integer> intArrayAsList(final int[] a) {
if (a == null)
throw new NullPointerException();
return new AbstractList<Integer>() {
public Integer get(int i) {
return a[i];
}
@Override
public Integer set(int i, Integer val) {
int oldVal = a[i];
a[i] = val;
return oldVal;
}
public int size() {
return a.length;
}
};
}
}
另外再举个Apache Shiro中的例子,在org.apache.shiro.realm.Realm接口中有这么一段说明:
Most users will not implement the Realm interface directly, but will extend one of the subclasses, {@link org.apache.shiro.realm.AuthenticatingRealm AuthenticatingRealm} or {@link org.apache.shiro.realm.AuthorizingRealm}, greatly reducing the effort requird to implement a Realm from scratch.
即,直接实现某个接口是个繁琐的工作。我们更建议使用其子类(当然,并不是必须),比如:
org.apache.shiro.realm.CachingRealm CachingRealm
org.apache.shiro.realm.AuthenticatingRealm AuthenticatingRealm
org.apache.shiro.realm.AuthorizingRealm
当然,也有简单实现类(simple implementation),比如:
org.apache.shiro.authc.pam.ModularRealmAuthenticator
下面是书中提供的编写skeletal的例子:
public abstract class AbstractMapEntry<K, V> implements Map.Entry<K, V> {
// Primitive operations
public abstract K getKey();
public abstract V getValue();
// Entries in modifiable maps must override this method
public V setValue(V value) {
throw new UnsupportedOperationException();
}
// Implements the general contract of Map.Entry.equals
@Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?, ?> arg = (Map.Entry) o;
return equals(getKey(), arg.getKey())
&& equals(getValue(), arg.getValue());
}
private static boolean equals(Object o1, Object o2) {
return o1 == null ? o2 == null : o1.equals(o2);
}
// Implements the general contract of Map.Entry.hashCode
@Override
public int hashCode() {
return hashCode(getKey()) ^ hashCode(getValue());
}
private static int hashCode(Object obj) {
return obj == null ? 0 : obj.hashCode();
}
}
相对于提供一个实现类,编写一个skeletal实现有一些限制。
首先必须了解接口中哪些是最基本的行为,并将其实现留给子类实现,skeletal则负责接口中的其他方法(或者一个都不实现)或者其特征相关的实现。
另外,skeletal类的价值在于继承,稍不注意就可能因为继承而破坏封装性。
为继承而设计的类还需要提供相应的文档,比如哪些方法是self-use、类实现了Serializable或者Clonnable等...
除了可以提供基本实现这一"优势",抽象类还有一个优势就是:抽象类的变化比接口的变化更容易。
即,在后续版本中为抽象类增加方法比接口增加方法更容易。
在不破坏实现类的情况下,在一个公有接口中增加方法,这是不可能的(即便下面是skeletal类,但一直detect下去总会有实现类存在)。
因此,设计接口是个技术活,一旦定下来就别想再变了。
抽象类和接口之间的选择,某种角度上可以说是易扩展性和灵活性之间的选择。
Java - 接口还是抽象类的更多相关文章
- java 接口和抽象类的区别
java 接口和抽象类的区别抽象类:1.含有抽象方法的类一定为抽象类,反过来抽象类,不一定含有抽象方法:2.抽象类必须用abstract来进行定义,抽象方法也必须用abstract来进行定义:3.抽象 ...
- 初探设计:Java接口和抽象类何时用?怎么用?
今天犯了个错: “接口变动,伤筋动骨,除非你确定只有你一个人在用”.哪怕只是throw了一个新的Exception.哈哈,这是我犯的错误. 一.接口和抽象类 类,即一个对象. 先抽象类,就是抽象出类的 ...
- Java接口和抽象类有什么区别,哪些时候用接口,哪些时候用抽象类?
Java接口和抽象类有什么区别,哪些时候用接口,哪些时候用抽象类? 2013-01-05 17:16:09| 分类: JAVA | 标签:java |举报|字号 订阅 下面比较一下两者的 ...
- Java接口和抽象类的区别
今天看到项目中,写了一个抽象类,里面有很多方法继承了这类,当调用这个接口时,采用的是这个抽象类去调方法的,当时一想,这个不就是我们说的Java的多态的特征: 继承:存在继承关系的子类和父类 重写:子类 ...
- java接口和抽象类
关于接口 1.创建一个接口,需要使用interface关键字. 2.实现一个接口,需要使用implements关键字. 3.接口的成员属性都是静态常量(默认public static final). ...
- java接口与抽象类的区别
接口可以是标志接口,里面没有任何常量和方法. 抽象类不一定必须有抽象方法,也可也没有方法,但含抽象方法的类必须被声明为抽象类. 在抽象层次结构中,Java接口在最上面,然后紧跟着抽象类,然后是一般类. ...
- Java接口和抽象类的实现方法
一.java中的接口本质上是加约束的抽象类 //抽象类 public abstract class AExample { public abstract int add(int x,int y); p ...
- Java 接口和抽象类差别
原文:http://blog.csdn.net/sunboard/article/details/3831823 1.概述 一个软件设计的好坏,我想非常大程度上取决于它的总体架构,而这个总体架构事实上 ...
- Java接口和抽象类的理解
接口和抽象类的相同之处就是 都会有抽象方法 抽象方法就是一个没有方法体 等待继承的子类完成的方法 然而接口比较严格 它的方法必须是抽象方法且是公开的 抽象类 可以有自己的属性 和 实体方法 首相用面向 ...
- java接口与抽象类
本片随笔讲讲java中接口与抽象类. 一,接口 1.什么是接口? 那在日常生活中接口是什么呢?就是两个对象之间进行连接的部分就是接口,就比如热水器与水管的接口一样,他可以确保不同的东西之间的顺利连接, ...
随机推荐
- Mybatis 的输入参数学习
mybatis 的输入参数: 指得就是parameterType的参数 这个参数就代表的sql语句中的输入参数 sql语句中的参数使用 有两种方式 : 使用#{} 获取: 8个基本数据类型 + S ...
- [性能分析]linux文件描述符
1.什么是文件和文件描述符 Linux中文件可以分为4种:普通文件.目录文件.链接文件和设备文件.1.普通文件是用户日常使用最多的文件,包括文本文件.shell脚本.二进制的可执行和各种类型的数据.l ...
- 机器学习笔记(五) K-近邻算法
K-近邻算法 (一)定义:如果一个样本在特征空间中的k个最相似的样本中的大多数属于某一个类别,则该样本也属于这个类别. (二)相似的样本,特征之间的值应该是相近的,使用k-近邻算法需要做标准化处理.否 ...
- 微信小程序富文本编辑,Thinkphp5.1
内容很简单,代码做了注释可以看一下 主要是把 文本 标题 标题颜色 图片 赋值到数组内, 举例: { pic: "/Share/image/images/bgqq_01.png" ...
- Springboot接口简单实现生成MySQL插入语句
Springboot接口简单实现调用接口生成MySQL插入语句 在实际测试中,有这样一个需求场景,比如:在性能压力测试中,可能需要我们事先插入数据库中一些相关联的数据. 我们在实际测试中,遇到问题,需 ...
- leetcode-268-Missing Number(异或)
题目描述: Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, find the one that is ...
- 数据挖掘 Apriori Algorithm python实现
该算法主要是处理关联分析的: 大多书上面都会介绍,这里就不赘述了: dataset=[[1,2,5],[2,4],[2,3],[1,2,4],[1,3],[2,3],[1,3],[1,2,3,5],[ ...
- 在Eclipse平台中,搭建SpringBoot开发环境
1.查看eclipse版本号,Help->About Eclipse IDE 2.下载对应版本的STS插件 下载地址:https://spring.io/tools3/sts/all(注意版本对 ...
- [转] String to Date conversion in hive - 在 Hive 中各种字符串转换成日期格式
[From] http://bigdataprogrammers.com/string-date-conversion-hive/ Please refer below table to conver ...
- (转)WordPress常用模板函数 修改或自制WordPress主题必备
对于很多WordPress新手来说,不懂任何代码的情况下去瞎改WordPress主题,得出的效果往往会出现语法错误之类的东西或效果不尽人意.想要修改 WordPress主题模板文件最基本的当然要懂得H ...