文档章节

抽象类与接口的区别及应用

突然帅了
 突然帅了
发布于 2015/04/06 08:11
字数 3594
阅读 12
收藏 0

抽象类(Abstract Class)与接口(Interface)是面向对象程序设计中两个重要的概念。由于两者在自身特性及应用方法上存在诸多相似性,如都不能实例化、都可以被继承(严格来说对于接口应该叫做实现),这么一来,在许多人心中抽象类与接口的界限非常模糊,对何时该使用抽象类、何时该使用接口更是感到困惑。

本文的目的是通过对两者的讨论与比较,帮助读者认清抽象类与接口在思想本质及应用场合方面的区别,如能做到这一点,读者便可以得心应手地根据具体情况正确选择和使用抽象类与接口。

1. 抽象类与接口是面向对象思想层面概念,不是程序设计语言层面概念

如若想正确认识抽象类与接口,首先要弄清楚的一点是,这两个概念均属于面向对象思想层面,而不属于某种程序设计语言。例如,C#中用interface关键字声明的语言元素,我们叫它“接口”,其实这是不准确的,准确来说,这应该叫做“接口在C#语言中的实现机制”。

面向对象思想包含许多概念,而不同面向对象语言对这些概念的具体实现机制各有不同。例如,C++中并没有一种关键字对应于C#中的interface,那么C++中就没有接口的概念了吗?非也!在C++中,如果想定义一个接口,可以通过将一个类中所有方法定义为纯虚方法[①]来做到。

这里可以看到,同样是接口,C#中用interface关键字来定义,而C++通过创建一个只包含纯虚方法的类来定义,这就是同一种概念在不同具体语言中具有不同的实现机制。类似的,C++中也没有abstract关键字用于定义抽象类,而是如果一个类中至少含有一个纯虚方法且它的方法不全为纯虚方法,则这个类被称为抽象类。

通过上面的分析可以看出,如果仅仅停留在语言层面去认知抽象类与接口,是无法准确理解两者的真谛的,因为不同语言对同一概念的实现机制有很大差别。如果一个C#初学者简单将两者理解为“用abstract修饰的类是抽象类,用interface定义的语言元素是接口”,那么当他接触C++时一定会感到困惑,因为C++里既没有abstract也没有interface,而是通过类中纯虚方法的情况确定这是个类、是个抽象类还是个接口。

明确了上面的问题,我们就可以给出抽象类与接口的真正定义了。

抽象类是不能实例化的类,但是其中的方法可以包含具体实现代码。

接口是一组方法声明的集合,其中应仅包含方法的声明,不能有任何实现代码。

以上对抽象类和接口的定义与任何具体语言无关,而是从面向对象思想角度进行的定义,不同语言可以有不同的实现机制。

从上面的定义中,我们可以发现两者在思想层面上的一项重大区别:抽象类是类(Class),接口是集合(Set,两者从本质上不是一种东西。这是我们总结出的第一个区别。请读者受累将上面加粗的字能放声朗十遍,声音越大越好,但是如果被室友或邻居扔鸡蛋请不要找我。

2. 抽象类是本体的抽象,接口是行为的抽象

在开始这一节之前,我想先请问各位一个问题,“我是一个人”和“我能呼吸”分别表达了“我”和“人”以及“我”和“呼吸”的关系,那么这两句话表达的是一种关系吗?如果你能很容易区分前者表示“是一个”的关系,而后者表示“能”的关系,那么恭喜你,你一定也能很容易区分抽象类和接口。

在阅读这一节时,请读者务必谨记上面这个问题以及下面这句话:

抽象类表示是一个(IS-A关系的抽象,接口表示能(CAN-DO关系的抽象。

请照例将上面的大声话朗读十遍。

好的,请各位擦干净头上的鸡蛋,我们继续。

从上面粗体字中我们可以看出,抽象类和接口有一个共性——它们都是“某种关系的抽象”,只不过类型不同罢了。其实如果将上面那句话的前半句中的“抽象类”改为“类”也是正确的,这并不奇怪,上文我们说过,抽象类只不过是一种特殊的类罢了。

下面我们先来解释IS-A关系。其实英语中的IS-A关系在汉语中可以解释为两种情况,当IS-A用在一个对象和一个类之间时,意思是“这个对象是类的一个实例”,例如关羽是一个对象,我们可以说“GuanYu IS-A General”,其中General(将军)是个类,这表示关羽是将军类的一个实例。而当IS-A用在两个类之间时,我认为叫做IS-A-KIND-OF更为准确,表示汉语中的“是一种”,如“General IS-A Person”,表示将军这个类是人这个类的一种,换用面向对象术语可以如下表述:General是Person的子类(Sub Type),Person是General的父类或超类(Super Type),General继承自Person。

这后一种IS-A关系,就是抽象类所表达的关系。分析到这里可以看出,抽象类所表达的关系其实就是面向对象三大特性之一——继承(Inheritance),也就是说,抽象类所表达的关系,与一般类与类之间的继承并无区别,而抽象类相比普通类,除了不能实例化外,也并无区别。之所以出现抽象类,是因为在较高抽象层次上,某些方法(往往是纯虚方法)无法实现,必须由其子类按照各自不同的情况具体实现。因为它含有纯虚方法,所以将这种类实例化在道理上讲不通,但我们又希望将这些子类中共有的部分抽象出来减少代码重复,于是就有了抽象类——它包含可复用部分,但又不允许实例化。

因此,抽象类的使用动机是在不允许实例化的限制下复用代码。请牢记这个动机。

接着再说说接口和CAN-DO关系。

我们知道,面向对象编程的基本思想就是通过对象间的相互协作,完成程序的功能。具体来说,在面向对象编程中,要求每个类都隐藏内部细节(这叫封装性),仅对外暴露一组公共方法,对象间就通过互相调用彼此的公共方法完成程序功能。

可以看到,面向对象思想中,对象和对象间根本不需要了解,调用者甚至可以完全不知道被调用者是谁,只要知道被调用者“能干什么”就行了。这就如同拨打110报警一样,你根本不知道对方长什么样、穿什么衣服、结没结婚、有没有孩子,你也不知道对方在哪,对象是谁,但是你知道对方一定“能接警”,所以你可以顺利完成报警。

这种“能干什么”就是CAN-DO关系,当我们把这种CAN-DO关系抽象出来,形成一个CAN-DO关系的集合,这就是接口了。那么使用接口的动机又是什么呢?动机之一是松散耦合。我们知道“低耦合”是面向对象程序设计中一个重要原则,而很大一部分耦合就是调用关系,面向对象中术语叫“依赖”。如果没有接口,调用者就要紧依赖于被调用者,就如同在没有110报警的年代,你只认识一个接警员,不知道其他接警员的电话,那么当你报警时,你必须给这个接警员打电话才行,如果哪天这个接警员休假或病了,你就无法报警了,除非你再去认识一个接警员。这时,我们说你紧依赖于这个接警员,也叫紧耦合。但有了110报警后就不一样了,我们将“可接警”看作一个接口,接口中有一个方法“接警”,而拨通110后,电话那头的人一定是实现了这个接口的,这时报警人不再依赖于具体接警员,而是依赖于“可接警”接口,这就叫做松依赖。

所以说,接口又可以看作一组规则的集合,它是对调用者的保证,对被调用者的约束。如上例中,可接警对报警人(调用者)保证调用对象可接警,同时约束接警部门必须把一个实现了这个接口的人安排在接警电话前面。哪怕这是个机器人或刚进行了两个小时接警培训的保洁员都没关系。

使用接口的另一个动机就是实现多态性[②]

下面想象你被分配到一个全新的研发小组做主管,第一天上班的早晨,一群人站在你面前等着你训话,你完全不认识他们,也不知道他们各自的职务,但是你可以说一句“都去工作吧”,于是大家作鸟兽散,程序员去写程序,会计去核对账目,业务员出门联系客户……当你这样做的时候,你就利用接口实现了多态性。因为你知道,他们都实现了“可工作”这个接口,虽然各个人员对“工作”具体的实现不一样,但这不要紧,你只要调用他们的“工作”方法,他们就各自做自己的事情了。如果你不能面向接口去利用多态性,你就要一个个说:“程序员去写程序,会计去核账,业务员快出门联系客户……”,这实在非常的费劲。

对这一节的内容做一个总结:

抽象类表示是一个(IS-A关系的抽象,它抽象了类的本体,其使用动机是在不允许实例化的限制下复用代码。接口表示能(CAN-DO关系的抽象,它抽象了类的行为,其使用动机是松散对象间的耦合以及实现程序多态性。

好的,照例念十遍吧,不过这次我允许你默念,因为我怕这次飞来的不是鸡蛋而是砖头。

经过上面的分析,我想你已经可以很容易在抽象类与接口间做出选择了。如果你是为了将一系列类的公共代码抽出,减少代码的重复,并且这些类与抽象出来的类可以表述为IS-A关系,就用抽象类;如果你是为了将一个或一组行为抽象出来,用以松散对象间耦合或实现多态性,那就用接口吧。

3. C#中抽象类与接口的探讨

这一节我们讨论C#语言中一个是人尽皆知的区别:C#中,一个类最多只能继承一个抽象类,但可以实现多个接口。

如果能充分理解抽象类对应于IS-A而接口对应于CAN-DO,则对这个约束不会感到奇怪。因为从逻辑上来说,一个类在所有相同抽象层次的类中只能“是其中一个”,但“能干多种事情”。这里的相同抽象层次指互相不存在继承关系的一个全集。

例如,{猪,牛,狗,猫} 可以看作具有相同抽象层次,其某个下层类只能是其中一个的子类,一个类不可能既是牛的子类又是猪的子类,但有可能既是牛的子类又是动物的子类,例如奶牛,这是因为“动物”与“牛”不在一个抽象层次上,“牛”本身就是“动物”的一个子类。

一般的,如果ClassAClassB的子类,同时也是ClassC的子类,那么一定存在ClassBClassC的子类或ClassCClassB的子类。

换句话说,一个类同时继承两个互相没有继承关系的类在逻辑上是不成立的。这就说明了为什么C#中不允许同时继承一个以上的抽象类。如果一个类要继承两个抽象类,那么从逻辑上来说,两个抽象类之间必然也存在继承关系,因此只需让该类继承较具体的那个抽象类即可。例如,本来的设计为“奶牛”同时继承“牛”和“动物”,但很容易发现,“牛”和“动物”已经存在继承关系,“牛”是继承于“动物”的,因此可将继承关系修改为“奶牛”只继承“牛”,而让“牛”继承于“动物”,这样就消除了多重继承。

而接口的CAN-DO关系在逻辑上不存在这样的矛盾,所以C#允许实现多个接口,具体为什么请读者自己思考。

顺便说一句,C++中允许多重继承是因为C++中非抽象类、抽象类和接口都用类来实现,而没有在语言层面区分成不同的语言元素,其实如果设计良好,也是不应该出现对抽象类的多重继承的,C#在语言层面上进行了约束,更有利于良好的设计,而C++对这方面比较灵活,需要开发者自己把握,因此C++对于初学者把握抽象类与方法更困难一些。


本文转载自:http://www.cnblogs.com/kingtiger/articles/2092350.html

突然帅了
粉丝 8
博文 90
码字总数 28698
作品 0
西安
私信 提问
abstract class和interface有什么区别?

含有abstract修饰符的class即为抽象类,abstract 类不能创建实例对象。含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必是抽象的。abstract class类中定义抽象方...

jdroid
2012/04/02
899
0
java中抽象类跟接口的区别

含有abstract修饰符的class 即为抽象类,abstract类不能创建实例对象,含有abstract的方法的类必须定义为abstract class ,abstract class 里的方法不必是抽象的,抽象来中定义抽象方法必须放...

的书法上的
2014/05/26
9
0
看大话设计模式,研究状态模式-- 搞明白抽象类与接口的区别

抽象类,是面向继承关系的。 子类可直接使用父类方法。 接口是面向方法行为规范统一的。 接口的每个子类需要做方法的实现。 而且是每个抽象方法的实现。 单继承,多接口。 .Net提供了接口,这...

马知常
2013/09/11
322
1
Java中抽象类和接口在概念、语法和应用上的区别和关系

春招开始了,盆友们都忙着准备笔试、准备面试,复习学过的知识点,当然我也不例外,在这里祝每一个"有心人"心想事成,梦圆2016,加油! 进入正题,今天总结了一下Java中抽象类和接口在概念、...

limiaoneu
2016/03/05
106
0
常见java面试题总结

1、String中的==和equals区别 答:==操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只...

ahucsxl
2015/08/30
128
0

没有更多内容

加载失败,请刷新页面

加载更多

可见性有序性,Happens-before来搞定

写在前面 上一篇文章并发 Bug 之源有三,请睁大眼睛看清它们 谈到了可见性/原子性/有序性三个问题,这些问题通常违背我们的直觉和思考模式,也就导致了很多并发 Bug 为了解决 CPU,内存,IO ...

tan日拱一兵
16分钟前
2
0
网络七层模型与TCP/UDP

为了使全球范围内不同的计算机厂家能够相互之间能够比较协调的进行通信,这个时候就有必要建立一种全球范围内的通用协议,以规范各个厂家之间的通信接口,这就是网络七层模型的由来。本文首先...

爱宝贝丶
19分钟前
2
0
Jenkins World 贡献者峰会及专家答疑展位

本文首发于:Jenkins 中文社区 原文链接 作者:Marky Jackson 译者:shunw Jenkins World 贡献者峰会及专家答疑展位 本文为 Jenkins World 贡献者峰会活动期间的记录 Jenkins 15周岁啦!Jen...

Jenkins中文社区
37分钟前
8
0
杂谈:面向微服务的体系结构评审中需要问的三个问题

面向微服务的体系结构如今风靡全球。这是因为更快的部署节奏和更低的成本是面向微服务的体系结构的基本承诺。 然而,对于大多数试水的公司来说,开发活动更多的是将现有的单块应用程序转换为...

liululee
51分钟前
7
0
OSChina 周二乱弹 —— 我等饭呢,你是不是来错食堂了?

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @ 自行车丢了:给主编推荐首歌 《クリスマスの夜》- 岡村孝子 手机党少年们想听歌,请使劲儿戳(这里) @烽火燎原 :国庆快来,我需要长假! ...

小小编辑
今天
561
10

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部