Java有两种机制可以为某个抽象提供多种实现——Interfaceabstract 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 - 接口还是抽象类的更多相关文章

  1. java 接口和抽象类的区别

    java 接口和抽象类的区别抽象类:1.含有抽象方法的类一定为抽象类,反过来抽象类,不一定含有抽象方法:2.抽象类必须用abstract来进行定义,抽象方法也必须用abstract来进行定义:3.抽象 ...

  2. 初探设计:Java接口和抽象类何时用?怎么用?

    今天犯了个错: “接口变动,伤筋动骨,除非你确定只有你一个人在用”.哪怕只是throw了一个新的Exception.哈哈,这是我犯的错误. 一.接口和抽象类 类,即一个对象. 先抽象类,就是抽象出类的 ...

  3. Java接口和抽象类有什么区别,哪些时候用接口,哪些时候用抽象类?

    Java接口和抽象类有什么区别,哪些时候用接口,哪些时候用抽象类? 2013-01-05 17:16:09|  分类: JAVA |  标签:java  |举报|字号 订阅     下面比较一下两者的 ...

  4. Java接口和抽象类的区别

    今天看到项目中,写了一个抽象类,里面有很多方法继承了这类,当调用这个接口时,采用的是这个抽象类去调方法的,当时一想,这个不就是我们说的Java的多态的特征: 继承:存在继承关系的子类和父类 重写:子类 ...

  5. java接口和抽象类

    关于接口 1.创建一个接口,需要使用interface关键字. 2.实现一个接口,需要使用implements关键字. 3.接口的成员属性都是静态常量(默认public static final). ...

  6. java接口与抽象类的区别

    接口可以是标志接口,里面没有任何常量和方法. 抽象类不一定必须有抽象方法,也可也没有方法,但含抽象方法的类必须被声明为抽象类. 在抽象层次结构中,Java接口在最上面,然后紧跟着抽象类,然后是一般类. ...

  7. Java接口和抽象类的实现方法

    一.java中的接口本质上是加约束的抽象类 //抽象类 public abstract class AExample { public abstract int add(int x,int y); p ...

  8. Java 接口和抽象类差别

    原文:http://blog.csdn.net/sunboard/article/details/3831823 1.概述 一个软件设计的好坏,我想非常大程度上取决于它的总体架构,而这个总体架构事实上 ...

  9. Java接口和抽象类的理解

    接口和抽象类的相同之处就是 都会有抽象方法 抽象方法就是一个没有方法体 等待继承的子类完成的方法 然而接口比较严格 它的方法必须是抽象方法且是公开的 抽象类 可以有自己的属性 和 实体方法 首相用面向 ...

  10. java接口与抽象类

    本片随笔讲讲java中接口与抽象类. 一,接口 1.什么是接口? 那在日常生活中接口是什么呢?就是两个对象之间进行连接的部分就是接口,就比如热水器与水管的接口一样,他可以确保不同的东西之间的顺利连接, ...

随机推荐

  1. c语言第一次作业--顺序、分支结构

    1.1思维导图 1.2.1本周学习体会以及代码量学习体会 1.2.2学习体会     因为在暑假时候没有对c语言进行学习,没太关注一些学习资料,一些教学视频也没看,感觉对c语言是陌生的,刚开课的时候自 ...

  2. OpenStack 业务链networking-sfc介绍 (1) - 概述

    原文链接:https://blog.csdn.net/bc_vnetwork/article/details/65630355 1.  Service Function Chain概述 Neutron ...

  3. 自注意力机制(Self-attention Mechanism)——自然语言处理(NLP)

    近年来,注意力(Attention)机制被广泛应用到基于深度学习的自然语言处理(NLP)各个任务中.随着注意力机制的深入研究,各式各样的attention被研究者们提出.在2017年6月google机 ...

  4. 使用Anaconda的python安装虚拟环境是出现错误:python -m venv venvdir----Error: Command '['D:\\Development\\Django\\test\\Scripts\\python.exe', '-Im', 'ensurepip', '--upgrade', '--default-pip']' returned non-zero exit

    在创建python虚拟环境的时候,如果使用的是Anaconda中集成的python -m venv venvdir就会出现不能安装pip的错误,原因是Anaconda没有ensurepip, 解决办法 ...

  5. 为何会有Python学习计划

    近几年感觉自己需要不断充电,从网上找寻技术潮流前端时Python映入眼帘,未来的技术,Python应该很有市场. 于是,以很低的成本从网上找到相关最新学习资料,希望自己未来的路,能坚持与书为伴,不断攀 ...

  6. java public class和class的区别

  7. CentOS7安装weblogic集群思路梳理

    以前经常用weblogic集群,但是却没有仔细想过要实现它.这不,前两天成功安装了weblogic集群,现在将其思路整理下.防止日后自己忘掉了. 一.安装weblogic10.3.6 1. 在官网下载 ...

  8. Code Chef IMPO(计算几何+扫描线+积分)

    题面 传送门 前置芝士 扫描线,积分求面积 题解 我怎么老是忘了积分可以求面积-- 首先,这两个投影的最小的\(x\)坐标和最大的\(x\)坐标必须相等,否则肯定无解 我们考虑一种方法,枚举\(x\) ...

  9. [CSS3] 动画暗角按钮

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  10. 【转载】Analysis Service Tabular Model #003 Multidimensional Model VS Tabular Model 我们该如何选择?

    由于Multidimensional Model 和 Tabular Model 并不能互相转换, 所以在项目之初就应该要考虑好选择哪一种模型进行开发. 以下只是一些建议: Licensing 许可和 ...