java--------抽象类与接口的区别

it2025-02-17  6

1、抽象类与接口的抽象层次是不同的 抽象类是对类抽象,接口是对行为抽象。类包含了属性与行为,所以说接口是更具体的抽象。

2、抽象类与接口的设计层次是不同的 抽象类是一种自下而上的设计,先有子类才能提取公同的属性与行为,抽象出父类,意思讲:子类的共同属性抽象出来的类就是父类,抽象类主要是规定/定义一个族群的祖先; 接口是一种自上而下的设计,先规定行为方法,只要可以实现这些行为,就可以成为接口的实现类,因为inteface具有多继承性。

3、抽象类与其派生类的关系和接口与其实现类的关系本质是不同的 抽象类与其派生类是一种“is-a”关系,即父类和派生子类在概念上的本质是相同的(父子关系,血缘联系,很亲密)。 接口与其实现类是一种“like-a”关系,即接口与实现类的关系只是实现了定义的行为,并无本质上的联系(契约关系,很淡漠的利益关系)。

举个例子:比如说一个动物抽象类,定义了跑的方法、叫的方法,但如果一个汽车类可以实现跑、可以实现叫,它就可以继承动物抽象类吗?!这太不合理了,汽车不是动物呀!而如果通过接口定义跑的方法、叫的方法,汽车类作为实现类实现跑和叫,完全OK很合理,就因为没有继承关系的约束。

为了更好的阐述他们之间的区别,下面将使用一个很棒的例子来说明。该例子引自博文链接。

我们有一个Door的抽象概念,它具备两个行为open()和close(),此时我们可以定义通过抽象类和接口来定义这个抽象概念:

抽象类:

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

接口:

interface Door{ void open(); void close(); } 1234 1234

至于其他的派生类可以通过: 1、使用extends使用抽象类方式定义Door 2、使用implements接口方式定义Door

这里发现两者并没有什么很大的差异,但是现在如果我们需要门具有报警的功能,那么该如何实现呢?

解决方案一:给Door增加一个报警方法:alarm();

abstract class Door{ abstract void open(); abstract void close(); abstract void alarm(); } 12345 12345

或者

interface Door{ void open(); void close(); void alarm(); } 12345 12345

这种方法违反了面向对象设计中的一个核心原则 ISP (Interface Segregation Principle)—见批注,在Door的定义中把Door概念本身固有的行为方法和另外一个概念”报警器”的行为方法混在了一起(意思就是说:Door(抽象类)的属性是所有门的共同特性,而具有报警器属性的门只有报警门所具有,如果把报警特性放到Door中,假设要定义一个不报警的门就需要修改Door,但是报警门又extends Door,所以这一改就会牵动很多其他的类,因此面向对象设计中有一个核心原则ISP)。这样引起的一个问题是那些仅仅依赖于Door这个概念的模块会因为”报警器”这个概念的改变而改变,反之依然。

解决方案二: 既然open()、close()和alarm()属于两个不同的概念,那么我们依据ISP原则将它们分开定义在两个代表两个不同概念的抽象类里面,定义的方式有三种: 1、两个都使用抽象类来定义。 2、两个都使用接口来定义。 3、一个使用抽象类定义,一个是用接口定义。

由于java不支持多继承所以第一种是不可行的。后面两种都是可行的,但是选择何种就反映了你对问题域本质的理解。

如果选择第二种都是接口来定义,那么就反映了两个问题: 1、我们可能没有理解清楚问题域,AlarmDoor在概念本质上到底是门还报警器。 2、如果我们对问题域的理解没有问题,比如我们在分析时确定了AlarmDoor在本质上概念是一致的,那么我们在设计时就没有正确的反映出我们的设计意图。因为你使用了两个接口来进行定义,他们概念的定义并不能够反映上述含义。

第三种,如果我们对问题域的理解是这样的:AlarmDoor本质上Door,但同时它也拥有报警的行为功能,这个时候我们使用第三种方案恰好可以阐述我们的设计意图。AlarmDoor本质是们,所以对于这个概念我们使用抽象类来定义,同时AlarmDoor具备报警功能,说明它能够完成报警概念中定义的行为功能,所以alarm可以使用接口来进行定义。如下:

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(){} } 1234567891011121314 1234567891011121314

这种实现方式基本上能够明确的反映出我们对于问题领域的理解,正确的揭示我们的设计意图。其实抽象类表示的是”is-a”关系,接口表示的是”like-a”关系,大家在选择时可以作为一个依据,当然这是建立在对问题领域的理解上的,比如:如果我们认为AlarmDoor在概念本质上是报警器,同时又具有Door的功能,那么上述的定义方式就要反过来了。

批注: ISP(Interface Segregation Principle):面向对象的一个核心原则。它表明使用多个专门的接口比使用单一的总接口要好。 一个类对另外一个类的依赖性应当是建立在最小的接口上的。 一个接口代表一个角色,不应当将不同的角色都交给一个接口。没有关系的接口合并在一起,形成一个臃肿的大接口,这是对角色和接口的污染

转载于:https://www.cnblogs.com/w-wfy/p/6409755.html

最新回复(0)