接口和抽象类之间到底有什么区别?
当前回答
我迟到10年了,但我想尝试任何方法。几天前写了一篇同样的帖子。我想把它张贴在这里。
tl;博士当您看到“Is A”关系时,请使用继承/抽象类。当您看到“有”关系时,创建成员变量。当您看到“依赖于外部提供程序”时,实现(而不是继承)一个接口。
面试问题:接口和抽象类有什么区别?你如何决定何时使用什么?我通常会得到以下一个或全部答案:答案1:不能创建抽象类和接口的对象。
ZK(这是我的首字母缩写):你不能创建任何一个对象。所以这并不是什么区别。这是接口和抽象类之间的相似之处。柜台问题:为什么不能创建抽象类或接口的对象?
答案2:抽象类可以有一个函数体作为部分/默认实现。
ZK:反问题:所以如果我将其更改为纯抽象类,将所有虚拟函数标记为抽象,并且不为任何虚拟函数提供默认实现。这会使抽象类和接口相同吗?之后它们可以互换使用吗?
答案3:接口允许多重继承,抽象类不允许。
ZK:反问:你真的从接口继承吗?还是只实现一个接口,从抽象类继承?实现和继承之间有什么区别?这些反问题会让考生们措手不及,让大多数人挠头,或者直接进入下一个问题。这让我觉得人们需要帮助来构建面向对象编程的基本构件。原始问题和所有反问题的答案都可以在英语和UML中找到。为了更好地理解这两个结构,您必须至少了解以下内容。
共同名词:共同名词是指同一类或同一种类的事物的“共同”名称。例如水果、动物、城市、汽车等。
专有名词:专有名词是物体、地点或事物的名称。苹果、猫、纽约、本田雅阁等。
汽车是一个通用名词。本田雅阁是一个专有名词,可能是一个合成专有名词,一个由两个名词组成的专有名词。
来到UML部分。您应该熟悉以下关系:
是A有A使用
让我们考虑下面的两句话。-本田雅阁是一辆车?-本田雅阁有车吗?
哪一个听起来正确?简单的英语和理解能力。本田雅阁(HondaAccord)和汽车(Cars)有着“是A”的关系。本田雅阁(Honda accord)没有车,它“是”一辆车。本田雅阁“有一个”音乐播放器。
当两个实体共享“Is A”关系时,它更适合继承。Has a relationship是创建成员变量的更好候选。这样,我们的代码如下所示:
abstract class Car
{
string color;
int speed;
}
class HondaAccord : Car
{
MusicPlayer musicPlayer;
}
现在本田不生产音乐播放器。或者至少这不是他们的主要业务。
因此,他们联系其他公司并签订合同。如果你在这里接收到电源和这两条电线上的输出信号,它在这些扬声器上会播放得很好。
这使得Music Player成为界面的完美候选。你不在乎谁为它提供支持,只要连接正常即可。
您可以用索尼或其他方式替换LG的MusicPlayer。这不会改变本田雅阁的任何事情。
为什么不能创建抽象类的对象?
因为你不能走进展厅说给我一辆车。你必须提供一个专有名词。什么车?可能是本田雅阁。这就是销售代理可以给你东西的时候。
为什么不能创建接口的对象?因为你不能走进展厅说给我一份音乐播放器的合同。这没用。接口位于消费者和提供者之间,只是为了促成协议。你将如何处理协议副本?它不会播放音乐。
为什么接口允许多重继承?
接口未被继承。接口已实现。界面是与外部世界交互的候选界面。本田雅阁有一个加油接口。它有给轮胎充气的接口。和用来给足球充气的软管一样。因此,新代码如下所示:
abstract class Car
{
string color;
int speed;
}
class HondaAccord : Car, IInflateAir, IRefueling
{
MusicPlayer musicPlayer;
}
英文的意思是“本田雅阁是一款支持轮胎充气和加油的汽车”。
其他回答
实际上并不是最初问题的答案,但一旦你找到了它们之间的区别的答案,你就会进入何时使用每一个困境:何时使用接口或抽象类?何时同时使用两者?
我对OOP的了解有限,但将接口视为语法中形容词的等价物对我来说一直有效(如果这个方法是假的,请纠正我!)。例如,接口名称类似于可以赋予类的属性或功能,并且类可以具有许多属性或功能:ISerializable、ICountable、IList、ICacheable、IHappy。。。
抽象类和接口的一般思想是由使用这些通用“设置”(某种模板)的其他类(不能单独构建)来扩展/实现,这使得为以后扩展它的所有对象设置特定的通用行为变得简单。
抽象类具有常规方法集和抽象方法。扩展类在被抽象类扩展后可以包括未设置的方法。当设置抽象方法时,它们由稍后扩展抽象方法的类定义。
接口与抽象类具有相同的财产,但只包含抽象方法,这些方法可以在其他类中实现(可以是多个要实现的接口),这就创建了一个更持久的方法/静态变量定义。与抽象类不同,您不能添加自定义的“常规”方法。
我想再加一个有意义的区别。例如,您有一个包含数千行代码的框架。现在,如果您想使用方法enhanceUI()在整个代码中添加新特性,那么最好在抽象类中添加该方法,而不是在接口中添加。因为,如果在接口中添加此方法,那么应该在所有已实现的类中实现它,但如果在抽象类中添加方法则不是这样。
抽象类与接口的主题主要是语义。
抽象类在不同的编程语言中通常充当接口的超集,除了一点,即可以实现多个接口,但只能继承一个类。
接口定义了某件事情必须能够做的事情;类似于合同,但不提供其实现。
抽象类定义了什么是什么,它通常托管子类之间的共享代码。
例如,格式化程序应该能够格式化()某些内容。描述这种情况的常见语义是创建一个接口IFormatter,该接口带有一个format()声明,其作用类似于一个契约。但是IFormatter并不描述什么是什么,而是描述它应该能够做什么。在本例中,我们创建一个抽象类。。。因此,我们创建了一个实现接口的抽象类Formatter。这是一个非常描述性的代码,因为我们现在知道我们有一个格式化程序,我们现在知道每个格式化程序必须能够做什么。
还有一个非常重要的主题是文档(至少对某些人来说…)。在您的文档中,您可能希望在子类中解释Formatter实际上是什么。有一个抽象类Formatter非常方便,您可以链接到子类中的文档。这是非常方便和通用的。另一方面,如果您没有抽象类Formatter,而只有接口IFormatter,则必须在每个子类中解释Formatter实际上是什么,因为接口是一个契约,你不会在接口的文档中描述Formatter实际上是什么-至少这不是常见的事情,你会打破大多数开发人员认为正确的语义。
注意:使抽象类实现接口是一种非常常见的模式。
让我们再次讨论这个问题:
首先要告诉你的是,1/1和1*1的结果是相同的,但这并不意味着乘法和除法是相同的。显然,他们有着良好的关系,但请注意,你们两个都不同。
我将指出主要的区别,其余的已经解释过了:
抽象类对于建模类层次结构非常有用。乍一看任何需求,我们都部分清楚要构建什么,但我们知道要构建什么。所以抽象类就是基类。
接口对于让其他层次结构或类知道我能做什么很有用。当你说我有能力做某事时,你必须有这种能力。接口将标记类必须实现相同的功能。