我最近接受了两次电话采访,被问及接口类和抽象类之间的区别。我已经解释了我能想到的每一个方面,但似乎他们在等我提一些具体的事情,我不知道是什么。

根据我的经验,我认为以下是正确的。如果我遗漏了一个要点,请告诉我。

接口:

接口中声明的每个方法都必须在子类中实现。接口中只能存在事件、委托、财产(C#)和方法。一个类可以实现多个接口。

抽象类:

子类只能实现抽象方法。抽象类可以具有具有实现的普通方法。除了事件、委托、财产和方法之外,抽象类还可以有类变量。由于C#中不存在多重继承,一个类只能实现一个抽象类。

在这之后,面试官提出了一个问题:“如果你有一个只有抽象方法的抽象类呢?这和接口有什么不同?”我不知道答案,但我认为这是上面提到的继承,对吧?另一位面试官问我,“如果你在接口中有一个公共变量,那会和抽象类中有什么不同?”我坚持认为你不能在接口中使用公共变量。我不知道他想听什么,但他也不满意。

另请参阅:

何时使用接口而不是抽象类,反之亦然接口与抽象类如何决定使用抽象类和接口?接口和抽象类之间的区别是什么?


当前回答

虽然您的问题表明它是针对“通用OO”的,但它似乎真正关注的是.NET对这些术语的使用。

在.NET中(类似于Java):

接口可以没有状态或实现实现接口的类必须提供该接口的所有方法的实现抽象类可以包含状态(数据成员)和/或实现(方法)抽象类可以在不实现抽象方法的情况下继承(尽管这样的派生类本身是抽象的)接口可能是多继承的,抽象类可能不是(这可能是接口与abtract类分开存在的关键具体原因——它们允许实现多继承,从而消除了一般MI的许多问题)。

作为通用OO术语,差异不一定定义明确。例如,有些C++程序员可能持有类似的严格定义(接口是抽象类的严格子集,不能包含实现),而有些人可能会说,具有某些默认实现的抽象类仍然是接口,或者非抽象类仍然可以定义接口。

事实上,有一种叫做非虚拟接口(NVI)的C++习惯用法,其中公共方法是“thunk”到私有虚拟方法的非虚拟方法:

http://www.gotw.ca/publications/mill18.htmhttp://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-虚拟界面(_I)

其他回答

简而言之:抽象类用于建模类似类的类层次结构(例如,Animal可以是抽象类,Human、Lion、Tiger可以是具体的派生类)

AND

接口用于2个相似/非相似类之间的通信,这些类不关心实现接口的类的类型(例如,Height可以是接口属性,它可以由Human、Building、Tree实现。无论你能不能吃东西,你能不能游泳,你能死什么都不重要。重要的是你需要有Height(在你的类中实现))。

虽然您的问题表明它是针对“通用OO”的,但它似乎真正关注的是.NET对这些术语的使用。

在.NET中(类似于Java):

接口可以没有状态或实现实现接口的类必须提供该接口的所有方法的实现抽象类可以包含状态(数据成员)和/或实现(方法)抽象类可以在不实现抽象方法的情况下继承(尽管这样的派生类本身是抽象的)接口可能是多继承的,抽象类可能不是(这可能是接口与abtract类分开存在的关键具体原因——它们允许实现多继承,从而消除了一般MI的许多问题)。

作为通用OO术语,差异不一定定义明确。例如,有些C++程序员可能持有类似的严格定义(接口是抽象类的严格子集,不能包含实现),而有些人可能会说,具有某些默认实现的抽象类仍然是接口,或者非抽象类仍然可以定义接口。

事实上,有一种叫做非虚拟接口(NVI)的C++习惯用法,其中公共方法是“thunk”到私有虚拟方法的非虚拟方法:

http://www.gotw.ca/publications/mill18.htmhttp://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-虚拟界面(_I)

接口:如果您希望在组件上暗示规则,则应使用该接口彼此相关的

赞成的意见:

允许多重继承通过不公开上下文中使用的确切类型的对象来提供抽象通过合同的特定签名提供一致性

欺骗:

必须执行所有定义的合同不能有变量或委托一旦定义,就不能在不破坏所有类的情况下进行更改

抽象类:应用于希望对彼此相关的组件具有一些基本或默认行为或实现的情况

赞成的意见:

比接口更快在实施中具有灵活性(您可以完全或部分实施)可以在不破坏派生类的情况下轻松更改

欺骗:

无法实例化不支持多重继承

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;
}

英文的意思是“本田雅阁是一款支持轮胎充气和加油的汽车”。

如果您认为java是OOP语言来回答这个问题,那么Java8的发布会导致上述答案中的一些内容过时。现在,java接口可以具有具有具体实现的默认方法。

Oracle网站提供了接口和抽象类之间的关键区别。

如果出现以下情况,请考虑使用抽象类:

您希望在几个密切相关的类之间共享代码。您希望扩展抽象类的类具有许多公共方法或字段,或者需要公共以外的访问修饰符(如protected和private)。您要声明非静态或非final字段。

如果出现以下情况,请考虑使用接口:

您希望不相关的类实现您的接口。例如,许多不相关的对象可以实现Serializable接口。您希望指定特定数据类型的行为,但不关心谁实现了它的行为。您希望利用类型的多重继承。

简单来说,我想使用

接口:通过多个不相关的对象实现合同

抽象类:在多个相关对象之间实现相同或不同的行为

看看代码示例,以清楚的方式理解事情:我应该如何解释接口和抽象类之间的区别?