深入理解abstract class与interface

    |     2015年5月5日   |   Java面向对象高级特性   |     0 条评论   |    1403

abstract class 和 interface 是 Java 里定义抽象类型的两种机制,也因此赋予了语言强大的面向对象能力。二者很像,有时还能互换,所以不少人选型很随意。

其实差别不小:选哪一个,往往反映出你对问题领域本质、对设计意图的理解是否站得住。下面按语法、编程和设计理念三层来拆。

一、什么是抽象类

这里的“抽象类”不只等于关键字 abstract class,而是指一个抽象体:类里没有足够信息描绘具体对象。它表征分析设计里得到的抽象概念——圆、三角形不同,但都属于“形状”;形状在问题领域里并不作为实物存在,所以不能实例化。

面向对象里,抽象类型主要用来做类型隐藏:固定一组行为的抽象描述,具体实现可以有任意多个派生类。模块依赖这个稳定抽象,从而可对修改关闭、对扩展开放(OCP)。

二、语法层面

用 abstract class 定义 Demo:

abstract class Demo {
    abstract void method1();
    abstract void method2();
    ...
}

用 interface 定义:

interface Demo {
    void method1();
    void method2();
    ...
}

abstract class 可以有自己的数据成员和非抽象方法;interface 在当时的 Java 里只能有 static final 数据(一般也不定义),方法都是抽象的。从某种意义上说,interface 是一种特殊的 abstract class。(Java 8 起接口可以有 default / static 方法,本文仍按原文的设计理念来选:is-a 用抽象类,like-a 用接口。)

对比 abstract class interface
继承次数 类只能 extends 一次 可以实现多个 interface
默认行为 可以给方法写默认实现 当时不能;后来才有 default
数据成员 可以有实例字段 常量 static final
设计含义 is a like a(实现契约)

三、编程层面

两者都能表达 design by contract,但用法不同。继承关系只能用一次,接口可以实施多份,这是语言对多重继承的折中。

抽象类能给方法默认行为;接口当时不行,绕过去往往要委托,复杂度上升。不能写默认行为还有维护问题:日后给界面加方法或加参数,派生类一多就很痛;若界面是 abstract class,有时只改父类默认实现即可。同样的实现若在每个派生类里复制一份,也违反 “one rule, one place”。

四、设计理念:Door 与报警

abstract class 体现继承,合理的继承要求父类与派生类存在 is a:概念本质上相同。interface 不要求实现者和接口在概念上是同一类事物,只要履行契约。

假设领域里有 Door,能 open / close:

abstract class Door {
    abstract void open();
    abstract void close();
}
interface Door {
    void open();
    void close();
}

看起来差不多。若还要求报警呢?

方案一:直接把 alarm 塞进 Door

abstract class Door {
    abstract void open();
    abstract void close();
    abstract void alarm();
}
interface Door {
    void open();
    void close();
    void alarm();
}
class AlarmDoor extends Door {
    void open() { ... }
    void close() { ... }
    void alarm() { ... }
}
class AlarmDoor implements Door {
    void open() { ... }
    void close() { ... }
    void alarm() { ... }
}

这违反 ISP(接口隔离):把 Door 固有行为和“报警器”混在一起。只依赖开门关门的模块,会因为报警概念的改动而跟着改。

方案二:两个概念拆开

open/close 与 alarm 属于不同概念,应分到两个抽象里。两个都用 abstract class 不可行——Java 不能多重继承。两个都用 interface,又说不清 AlarmDoor 本质上到底是门还是报警器,也反映不出设计意图。

若理解是:AlarmDoor 本质上是 Door,同时又有报警能力,则 Door 用抽象类(is a),报警用接口(like a):

abstract class Door {
    abstract void open();
    abstract void close();
}
interface Alarm {
    void alarm();
}
class AlarmDoor extends Door implements Alarm {
    void open() { ... }
    void close() { ... }
    void alarm() { ... }
}

这基本能反映对领域的理解和设计意图。若你认为 AlarmDoor 本质上是报警器、顺便能当门用,定义就要反过来。

一句话总结:都能定义抽象类型,但 abstract class 说的是 is-a,interface 说的是 like-a;选型先问清概念本质,再写代码。

转载请注明来源:深入理解abstract class与interface
本文链接地址:https://ai.zhousir.top/?p=616
回复 取消