标题:
Java 抽象类与接口(3)抽象类与接口的区别
[打印本页]
作者:
look_w
时间:
2018-12-15 14:15
标题:
Java 抽象类与接口(3)抽象类与接口的区别
三、抽象类与接口的区别尽管抽象类和接口之间存在较大的相同点,甚至有时候还可以互换,但这样并不能弥补他们之间的差异之处。下面将从语法层次和设计层次两个方面对抽象类和接口进行阐述。
3.1语法层次在语法层次,java 语言对于抽象类和接口分别给出了不同的定义。下面已 Demo 类来说明他们之间的不同之处。
[url=]
[/url]
使用抽象类来实现:
public
abstract
class
Demo {
abstract
void
method1();
void
method2(){
//
实现
} }使用接口来实现
interface
Demo {
void
method1();
void
method2(); }
[url=]
[/url]
抽象类方式中,抽象类可以拥有任意范围的成员数据,同时也可以拥有自己的非抽象方法,但是接口方式中,它仅能够有静态、不能修改的成员数据(但是我们一般是不会在接口中使用成员数据),同时它所有的方法都必须是抽象的。在某种程度上来说,接口是抽象类的特殊化。
对子类而言,它只能继承一个抽象类(这是 java 为了数据安全而考虑的),但是却可以实现多个接口。
3.2设计层次上面只是从语法层次和编程角度来区分它们之间的关系,这些都是低层次的,要真正使用好抽象类和接口,我们就必须要从较高层次来区分了。只有从设计理念的角度才能看出它们的本质所在。一般来说他们存在如下三个不同点:
1、 抽象层次不同。抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
2、 跨域不同。抽象类所跨域的是具有相似特点的类,而接口却可以跨域不同的类。我们知道抽象类是从子类中发现公共部分,然后泛化成抽象类,子类继承该父类即可,但是接口不同。实现它的子类可以不存在任何关系,共同之处。例如猫、狗可以抽象成一个动物类抽象类,具备叫的方法。鸟、飞机可以实现飞 Fly 接口,具备飞的行为,这里我们总不能将鸟、飞机共用一个父类吧!所以说抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a"关系,即父类和派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的, 仅仅是实现了接口定义的契约而已。
3、 设计层次不同。对于抽象类而言,它是自下而上来设计的,我们要先知道子类才能抽象出父类,而接口则不同,它根本就不需要知道子类的存在,只需要定义一个规则即可,至于什么子类、什么时候怎么实现它一概不知。比如我们只有一个猫类在这里,如果你这是就抽象成一个动物类,是不是设计有点儿过度?我们起码要有两个动物类,猫、狗在这里,我们在抽象他们的共同点形成动物抽象类吧!所以说抽象类往往都是通过重构而来的!但是接口就不同,比如说飞,我们根本就不知道会有什么东西来实现这个飞接口,怎么实现也不得而知,我们要做的就是事前定义好飞的行为接口。所以说抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。
(上面纯属个人见解,如有出入、错误之处,望各位指点!!!!)
为了更好的阐述他们之间的区别,下面将使用一个例子来说明。该例子引自:
我们有一个 Door 的抽象概念,它具备两个行为 open() 和 close(),此时我们可以定义通过抽象类和接口来定义这个抽象概念:
抽象类:
[url=]
[/url]
abstract
class
Door{
abstract
void
open();
abstract
void
close(); }接口
interface
Door{
void
open();
void
close(); }
[url=]
[/url]
至于其他的具体类可以通过使用 extends 使用抽象类方式定义 Door 或者 Implements 使用接口方式定义 Door,这里发现两者并没有什么很大的差异。
但是现在如果我们需要门具有报警的功能,那么该如何实现呢?
解决方案一:给 Door 增加一个报警方法:clarm();
[url=]
[/url]
abstract
class
Door{
abstract
void
open();
abstract
void
close();
abstract
void
alarm(); }或者
interface
Door{
void
open();
void
close();
void
alarm(); }
[url=]
[/url]
这种方法违反了面向对象设计中的一个核心原则 ISP (Interface Segregation Principle)—见批注,在 Door 的定义中把 Door 概念本身固有的行为方法和另外一个概念"报警器"的行为方 法混在了一起。这样引起的一个问题是那些仅仅依赖于 Door 这个概念的模块会因为”报警器”这个概念的改变而改变,反之依然。
解决方案二
既然 open()、close() 和alarm() 属于两个不同的概念,那么我们依据 ISP 原则将它们分开定义在两个代表两个不同概念的抽象类里面,定义的方式有三种:
1、两个都使用抽象类来定义。
2、两个都使用接口来定义。
3、一个使用抽象类定义,一个是用接口定义。
由于 Java 不支持多继承所以第一种是不可行的。后面两种都是可行的,但是选择何种就反映了你对问题域本质的理解。
如果选择第二种都是接口来定义,那么就反映了两个问题:1、我们可能没有理解清楚问题域,AlarmDoor 在概念本质上到底是门还报警器。2、如果我们对问题域的理解没有问题,比如我们在分析时确定了 AlarmDoor 在本质上概念是一致的,那么我们在设计时就没有正确的反映出我们的设计意图。因为你使用了两个接口来进行定义,他们概念的定义并不能够反映上述含义。
第三种,如果我们对问题域的理解是这样的:AlarmDoor 本质上 Door,但同时它也拥有报警的行为功能,这个时候我们使用第三种方案恰好可以阐述我们的设计意图。AlarmDoor 本质是们,所以对于这个概念我们使用抽象类来定义,同时 AlarmDoor 具备报警功能,说明它能够完成报警概念中定义的行为功能,所以 alarm 可以使用接口来进行定义。如下:
[url=]
[/url]
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(){} }
[url=]
[/url]
这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其实抽象类表示的是"is-a"关系,接口表示的是"like-a"关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为 AlarmDoor 在概念本质上是报警器,同时又具有 Door 的功能,那么上述的定义方式就要反过来了。
批注: ISP(Interface Segregation Principle)`:面向对象的一个核心原则。它表明使用多个专门的接口比使用单一的总接口要好。
一个类对另外一个类的依赖性应当是建立在最小的接口上的。
一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染。
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/)
Powered by Discuz! 7.0.0