接口与抽象类(通用OO)

最近有两次电话面试,他们问我Interface类和抽象类之间的区别。我已经解释了我能想到的每一个方面,但他们似乎在等我提一些具体的东西,我不知道是什么。

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

接口说明

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

抽象类:

只有抽象方法必须由子类实现。抽象类可以有带实现的普通方法。抽象类也可以有除了事件、委托、属性和方法之外的类变量。一个类只能实现一个抽象类,因为C#中不存在多重继承。

  1. 在这之后,面试官提出了一个问题“如果你有一个只有抽象方法的抽象类怎么办?这与接口有什么不同?”我不知道答案,但我认为这是上面提到的继承,对吧?

  2. 另一个面试官问我,“如果你在界面中有一个公共变量,那会和抽象类有什么不同?”我坚持认为你不能在界面中有一个公共变量。我不知道他想听什么,但他也不满意。

另见

763836 次浏览

还有其他一些不同之处——

接口不能有任何具体的实现。抽象基类可以。这允许你在那里提供具体的实现。这可以允许抽象基类实际上提供更严格的契约,而接口实际上只描述了类的使用方式。(抽象基类可以有定义行为的非虚拟成员,这给了基类作者更多的控制权。)

一个类可以实现多个接口。一个类只能从单个抽象基类派生。这允许使用接口实现多态层次结构,但不能使用抽象基类。这也允许使用接口进行伪多继承。

在v2+中可以修改抽象基类而不会破坏API。对接口的更改就是破坏更改。

[C#/. NET特定]接口与抽象基类不同,可以应用于值类型(结构)。结构不能从抽象基类继承。这允许在值类型上应用行为契约/使用指南。

对于. Net,

你对第二个面试官的回答也是对第一个面试官的回答……抽象类可以有实现,AND状态,接口不能……

编辑:另一方面,我甚至不会用“子类”(或“继承”短语)来描述“定义为实现”接口的类。对我来说,接口是类必须遵守的契约的定义,如果它被定义为“实现”该接口。它不继承任何东西…你必须自己添加所有东西,明确地。

面试官们正在寻找一棵奇怪的树。对于像C#和Java这样的语言,有区别,但在像C++这样的其他语言中没有区别。面向对象理论并没有区分这两者,只是语言的语法。

抽象类是具有实现和接口(纯虚方法)的类,它们将被继承。接口通常没有任何实现,只有纯虚函数。

在C#或Java没有任何实现的抽象类与接口的区别仅在于用于继承它的语法以及您只能从一个接口继承的事实。

我的另一个答案开始,主要处理何时使用一个与另一个:

根据我的经验,接口是最好的当你有几个类时使用每个人都需要对相同的方法或方法,以便它们可以可与其他代码互换使用这将被写在那些类的公共接口。最好的使用接口是当协议很重要,但底层逻辑可能不同于每节课。如果你不这样做复制逻辑,考虑抽象类或标准类继承

1)一个接口可以看作是一个纯抽象类,是一样的,但尽管如此,实现一个接口和从一个抽象类继承是不一样的。当你从这个纯抽象类继承时,你是在定义一个层次结构->继承,如果你实现了你不是的接口,你可以实现任意多的接口,但你只能从一个类继承。

2)您可以在接口中定义一个属性,因此实现该接口的类必须具有该属性。

例如:

  public interface IVariable{string name {get; set;}}

实现该接口的类必须具有类似的属性。

通过实现接口,你实现了组合(“has-a”关系)而不是继承(“is-a”关系)。这是一个重要的原则,当涉及到像设计模式这样的事情时,你需要使用接口来实现行为的组合而不是继承。

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

在. NET中(类似于Java):

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

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

事实上,有一个C++的习语称为非虚拟接口(NVI),其中公共方法是非虚拟方法,它们“thunk”到私有虚拟方法:

回答第二个问题:interface中定义的public变量默认为static final,而abstract类中的public变量是实例变量。

接口是执行特定行为的轻量级方式。这是一种思考方式。

还有一些其他的区别:

抽象类可以有静态方法、属性、字段等,运算符、接口不能。转换运算符允许转换到/从抽象类,但不允许转换到/从接口。

所以你几乎可以自己使用抽象类,即使它从来没有实现过(通过它的静态成员),并且你不能以任何方式单独使用接口。

打个比方:当我在空军的时候,我参加了飞行员培训,成为了一名美国空军飞行员。那时我没有任何飞行资格,不得不参加飞机型号培训。一旦我获得资格,我是一名飞行员(抽象类)和一名C-141飞行员(具体类)。在我的一项任务中,我被赋予了一项额外的职责:安全官。现在我仍然是一名飞行员和一名C-141飞行员,但我也履行了安全官的职责(可以说,我实施了ISafety警官)。飞行员不需要成为安全官,其他人也可以做到这一点。

所有美国空军飞行员都必须遵守某些空军范围的规定,所有C-141(或F-16或T-38)飞行员都是美国空军飞行员。任何人都可以成为安全官。所以,总结一下:

  • Pilot:抽象类
  • C-141 Pilot:具体类
  • 安全干事:接口

补充说明:这是一个有助于解释概念的类比,而不是编码建议。请参阅下面的各种评论,讨论很有趣。

我认为他们正在寻找的答案是基本的或OPPS的哲学差异。

当派生类共享抽象类的核心属性和行为时,使用抽象类继承。实际定义类的那种行为。

另一方面,当类共享外围行为时使用接口继承,这些行为不一定定义派生类。

例如,汽车和卡车共享汽车抽象类的许多核心属性和行为,但它们也共享一些外围行为,如生成排气,即使像Drillers或PowerGenerators这样的非汽车类也共享,并且不一定定义汽车或卡车,所以Car,Truck,Driller和PowerGenerator都可以共享相同的接口IExhaust。

  1. 接口:
    • 我们不实现(或定义)方法,我们在派生类中这样做。
    • 我们不在接口中声明成员变量。
    • 接口表示HAS-A关系。这意味着它们是对象的掩码。
  2. 抽象类:
    • 我们可以在抽象类中声明和定义方法。
    • 我们隐藏了它的构造函数。这意味着没有直接从它创建的对象。
    • 抽象类可以保存成员变量。
    • 派生类继承到抽象类,这意味着派生类中的对象没有被掩码,它继承到抽象类。在这种情况下的关系是IS-A。

这是我的看法。

从概念上讲,通过使用任何人或两者来保持语言特定的实现、规则、好处和实现任何编程目标,可以或不能有代码/数据/属性,等等,单一或多重继承,都不重要

1-抽象(或纯抽象)类是用来实现层次结构的。如果你的业务对象在结构上看起来有点相似,代表父子(层次结构)类型的关系,那么只有继承/抽象类才会被使用。如果你的业务模型没有层次结构,那么不应该使用继承(这里我不是在谈论编程逻辑,例如一些设计模式需要继承)。从概念上讲,抽象类是在OOP中实现业务模型层次结构的方法,它与接口无关,实际上比较抽象类和接口是没有意义的,因为两者在概念上是完全不同的东西,在访谈中被问到只是为了检查概念,因为它看起来都提供了一些相同的功能,当涉及到实现时,我们程序员通常更强调编码。

接口是一种契约,由一组或一组函数表示的完整业务功能。这就是为什么它被实现而不是继承。一个业务对象(无论是否属于层次结构)可以有任意数量的完整业务功能。一般来说,抽象类与继承无关。例如,人可以RUN,大象可以RUN,鸟可以RUN,等等,所有这些不同层次结构的对象都将实现RUN接口或EAT或SPEAK接口。不要进入实现,因为你可能会实现为实现这些接口的每种类型都有抽象类。任何层次结构的对象都可以具有与其层次结构无关的功能(接口)。

接口不是为了实现多重继承或公开公共行为而发明的,类似地,纯抽象类不是为了推翻接口,而是接口是一个对象可以做的功能(通过该接口的函数),抽象类代表层次结构的父级,以产生具有父级核心结构(属性+功能)的子级

当你被问及差异时,它实际上是概念上的差异,而不是特定于语言实现的差异,除非明确提出。

我相信,两位面试官都期望这两者之间有一条直截了当的区别,当你失败时,他们试图通过实施一个作为另一个来驱使你走向这种差异

如果您有一个只有抽象方法的抽象类怎么办?

短:抽象类用于建模类似类的类层次结构(例如,动物可以是抽象类,人类,狮子,老虎可以是具体的派生类)

Interface用于2个相似/不相似类之间的沟通,这些类不关心实现Interface的类的类型(例如,Height可以是接口属性,它可以由Human、Building、Tree实现。如果你能吃东西,你可以游泳,你可以死或任何东西都没关系…

虽然这个问题很老,但我想补充一点支持接口:

接口可以使用任何依赖注入工具注入,其中抽象类注入支持的很少。

继承
考虑一辆汽车和一辆公共汽车。它们是两种不同的车辆。但是,它们仍然有一些共同的属性,比如转向、刹车、齿轮、发动机等。
因此,使用继承概念,这可以表示为以下…

public class Vehicle {private Driver driver;private Seat[] seatArray; //In java and most of the Object Oriented Programming(OOP) languages, square brackets are used to denote arrays(Collections).//You can define as many properties as you want here ...}

现在一辆自行车…

public class Bicycle extends Vehicle {//You define properties which are unique to bicycles here ...private Pedal pedal;}

一辆车…

public class Car extends Vehicle {private Engine engine;private Door[] doors;}

这就是关于继承的全部内容。我们使用它们将对象分类为更简单的Base表单及其子表单,如上所述。

抽象类

抽象类是不完整对象。为了进一步理解它,让我们再次考虑车辆类比。
车辆可以被驾驶。对吧?但是不同的车辆以不同的方式驾驶……例如,你不能像驾驶自行车一样驾驶汽车。那么如何表示车辆的驱动功能呢?很难检查它是什么类型的车辆并用自己的功能驱动它;当添加新类型的车辆时,您必须一次又一次地更改Driver类。抽象类和方法的作用来了。您可以将驱动方法定义为抽象,以告知每个继承子类都必须实现此功能。
所以如果你修改车辆类别…

//......Code of Vehicle Classabstract public void drive();//.....Code continues

自行车和汽车还必须指定如何驾驶它。否则,代码将无法编译并抛出错误。
简而言之…抽象类是一个部分不完整的类,具有一些不完整的函数,继承的子类必须指定自己的函数。

接口接口是完全不完整的。它们没有任何属性。它们只是表明继承的孩子能够做一些事情…
假设你随身携带不同类型的手机,每种手机都有不同的方式来实现不同的功能;

public interface Dialable {public void dial(Number n);}

在这里,拨号器的制造者定义了如何拨打一个号码。你只需要给它一个号码来拨打。

// Makers define how exactly dialable work inside.
Dialable PHONE1 = new Dialable() {public void dial(Number n) {//Do the phone1's own way to dial a number}}
Dialable PHONE2 = new Dialable() {public void dial(Number n) {//Do the phone2's own way to dial a number}}

//Suppose there is a function written by someone else, which expects a Dialable......public static void main(String[] args) {Dialable myDialable = SomeLibrary.PHONE1;SomeOtherLibrary.doSomethingUsingADialable(myDialable);}.....

因此,使用接口而不是抽象类,使用Dialable的函数的作者不必担心它的属性。例如:它有触摸屏还是拨号盘,它是固定电话还是移动电话。你只需要知道它是否是可拨号的;它是否继承(或实现)了Dialable接口。

更重要的是,如果有一天你用不同的拨号切换

......public static void main(String[] args) {Dialable myDialable = SomeLibrary.PHONE2; // <-- changed from PHONE1 to PHONE2SomeOtherLibrary.doSomethingUsingADialable(myDialable);}.....

你可以确定代码仍然可以完美地工作,因为使用可拨号的函数不(也不能)依赖于可拨号接口中指定的细节之外的细节。它们都实现了一个可拨号接口,这是函数唯一关心的事情。

接口通常被开发人员用来确保对象之间的互操作性(可互换使用),只要它们共享一个共同的功能(就像你可能换到固定电话或手机,只要你只需要拨打一个号码)。简而言之,接口是抽象类的简单得多的版本,没有任何属性。
另外,请注意,您可以实现(继承)任意数量的接口,但您只能扩展(继承)一个父类。

更多信息抽象类vs接口

After all that, the interviewer came up with the question "What if you had anAbstract class with only abstract methods? How would that be differentfrom an interface?"

文档明确表示,如果抽象类只包含抽象方法声明,则应该将其声明为接口。

An another interviewer asked me what if you had a Public variable insidethe interface, how would that be different than in Abstract Class?

接口中的变量默认是公共静态和最终的。问题可以被框定为如果抽象类中的所有变量都是公共的怎么办?与接口中的变量不同,它们仍然可以是非静态和非最终的。

最后,我想再补充一点——抽象类仍然是类,属于单个继承树,而接口可以存在于多个继承中。

无论实现哪个类,都必须遵循接口的所有规范。

一个实时的例子是任何ISO标记的产品。ISO给出了一组rules/specification关于产品应该如何构建以及minimum set of featuresMust有什么。

这只是subset of properties产品必须have.ISO将签署产品only if it satisfies the its standards

现在看看这个代码

public interface IClock{       //defines a minimum set of specification which a clock should have
public abstract Date getTime();public abstract int getDate();}public class Fasttrack: Clock {// Must have getTime() and getTime() as it implements IClock// It also can have other set of feature likepublic void startBackgroundLight() {// watch with internal light in it.}.... //Fastrack can support other feature as well........}

这里一个Fastrack被称为手表,因为it has all that features that a watch must suppost最小特征集)。

为什么和何时抽象:

来自MSDN:

abstract class的目的是提供多个派生类可以共享的common definition of a base class

例如,类库可以定义一个抽象类,该类用作其许多函数的参数,并要求使用该库的程序员通过创建派生类来提供他们自己的类实现。Abstract simply means if you cannot define it completely declare it as an abstract。实现类将完成此实现。

例如-:假设我声明一个类Recipe为抽象的,但我不知道是哪个配方然后我将泛化这个类来定义common definition of any recipe配方的植入将取决于实施菜。

抽象类可以由抽象方法组成,也可以由非抽象方法组成。所以你可以注意到Interface中的区别。

简单的单词如果你想要tight coupling use Interface o/w use in case of lose coupling Abstract Class

接口定义了一个服务或一组服务的契约。它们以水平方式提供多态,因为两个完全不相关的类可以实现同一个接口,但可以互换用作它们实现的接口类型的参数,因为两个类都承诺满足接口定义的服务集。接口不提供实现细节。

抽象类为其子类定义了基本结构,也可以选择部分实现。抽象类以垂直但有方向的方式提供多态性,因为任何继承抽象类的类都可以被视为该抽象类的实例,而不是相反。抽象类可以而且经常包含实现细节,但不能单独实例化——只有它们的子类可以被“新”。

注意,C#也允许接口继承。

从编码的角度

如果抽象类只有抽象方法,接口可以替换抽象类。否则,将抽象类更改为接口意味着您将失去继承提供的代码可重用性。

从设计角度

如果它是“是”关系,并且您需要一个子集或所有功能,请将其保留为抽象类。如果它是“应该做”关系,请将其保留为接口。

决定你需要什么:只是策略执行,或者代码可重用性和策略。

大多数答案都集中在抽象类和接口之间的技术差异,但从技术上讲,接口基本上是一种抽象类(没有任何数据或实现),我认为概念的区别更有趣,这可能是面试官所追求的。

接口分成协议。它指定:“这是我们如何相互交谈”。它不能有任何实现,因为它不是应该有任何实现。这是一个契约。就像C中的.h头文件一样。

抽象类执行不全。一个类可能实现也可能不实现接口,抽象类不必完全实现它。没有任何实现的抽象类有点无用,但完全合法。

基本上任何类,无论抽象与否,都是关于它的,而接口是关于它的你如何使用它。例如:Animal可能是一个抽象类,实现一些基本的代谢功能,并指定抽象的呼吸和运动方法,而没有给出实现,因为它不知道应该通过鳃还是肺呼吸,以及它是飞行、游泳、行走还是爬行。另一方面,Mount可能是一个接口,它指定你可以骑动物,而不知道它是什么动物(或者它到底是不是动物!)。

在幕后,接口基本上是一个抽象类,只有抽象方法,这并不重要。从概念上讲,它们扮演着完全不同的角色。

由Jeffrey Richter通过C#从CLR复制…

我经常听到这个问题,“我应该设计一个基本类型还是一个接口?”答案并不总是明确的。

以下是一些可以帮助您的指南:

■■IS-A vs. CAN-DO关系类型只能继承一种实现。如果派生type不能声明与基类型的IS-A关系,不要使用基类型;使用接口。接口暗示CAN-DO关系。如果CAN-DO功能似乎属于对于各种对象类型,请使用接口。例如,类型可以转换自身的实例对于另一个类型(IConverable),一个类型可以序列化自己的实例(ISerializable),等等。请注意,值类型必须派生自System. ValueType,因此,它们不能从任意基类派生。在这种情况下,您必须使用CAN-DO关系并定义一个接口。

■■易用性作为开发人员,您通常更容易定义从基类型而不是实现接口的所有方法。基类型可以提供一个它包含了大量的功能,因此派生类型可能只需要对其行为进行相对较小的修改。如果提供接口,则新类型必须实现所有成员。

■■一致的实现无论接口契约记录得多好,它都每个人都不太可能100%正确地执行合同。事实上,COM遭受这个问题,这就是为什么一些COM对象只能正确工作微软Word或Windows Internet Explorer。通过提供具有良好默认实现,您开始使用一种有效且经过良好测试的类型;然后您可以修改需要修改的部分。

■■版本控制如果向基类型添加方法,派生类型将继承新方法,您从使用有效的类型开始,用户的源代码甚至不必重新编译。向接口添加新成员会强制接口的继承者更改它的源代码并重新编译。

我将解释接口和抽象的深度细节class.if你知道接口和抽象类的概述,然后第一个问题出现在你的脑海中,我们应该使用接口,什么时候我们应该使用抽象类。所以请检查下面的接口和抽象类的解释。

  1. 什么时候使用Interface?

    如果你不了解实现,只有我们有需求规范,那么我们使用接口

  2. 什么时候应该使用抽象类?

    如果您知道实现但不完全(部分实现),那么我们使用抽象类。

    接口

    默认情况下,每个方法的公共抽象意味着接口是100%纯抽象的。

    摘要

    可以有具体方法和抽象方法,什么是具体方法,在抽象类中有实现,抽象类是声明为抽象的类-它可能包含也可能不包含抽象方法。

    接口

    我们不能将接口声明为私有的、受保护的

    问:为什么我们不宣布接口是私有和受保护的?

    因为默认情况下接口方法是公共抽象的,所以我们没有将接口声明为私有和受保护的。

    接口方法
    此外,我们不能将接口声明为私有、受保护、最终、静态、同步、本机……

    我将给出原因:为什么我们不声明同步方法,因为我们不能创建接口对象和同步是在对象上工作,所以我们不声明同步方法的原因瞬态概念也不适用,因为瞬态工作与同步。

    摘要

    我们很高兴地使用公共,私有的最终静态……意味着没有任何限制在抽象中适用。

    接口

    变量在Interface中声明为默认的公共静态Final,因此我们也不会将变量声明为私有的、受保护的。

    Volatile修饰符也不适用于接口,因为接口变量默认是公共静态的最终和最终变量,一旦它将值分配给变量,您就不能更改值,一旦您将变量声明为接口,您就必须分配变量。

    易失性变量是不断变化的,所以它是opp。最后,这就是我们在接口中不使用易失性变量的原因。

    摘要

    抽象变量不需要声明公共静态最终。

希望这篇文章有用。

接口:如果您想暗示组件上的规则,则应使用该规则,该规则可能是也可能不是彼此相关

优点:

  1. 允许多重继承
  2. 通过不暴露上下文中正在使用的确切类型的对象来提供抽象
  3. 通过合同的特定签名提供一致性

缺点:

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

抽象类:应该用在你希望有一些基本的或默认的行为或实现相互关联的组件的地方

优点:

  1. 比界面快
  2. 在实现中具有灵活性(您可以完全或部分实现它)
  3. 可以在不破坏派生类的情况下轻松更改

缺点:

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

接口类型与抽象基类

改编自Pro C#5.0和. NET 4.5 Framework的书。

接口类型可能看起来与抽象基类非常相似当一个类被标记为抽象时,它可以定义任意数量的抽象成员来提供所有派生类型的多态接口。然而,即使一个类确实定义了一组抽象成员,它也可以自由定义任意数量的构造函数、字段数据、非抽象成员(使用另一方面,接口只包含抽象成员定义。由抽象父类建立的多态接口有一个主要限制因为只有派生类型支持抽象父级定义的成员。但是,在更大的在软件系统中,开发多个没有共同父级的类层次结构是很常见的鉴于抽象基类中的抽象成员仅适用于派生类型,我们无法配置不同层次结构中的类型以支持相同的多态接口。例如,假设您定义了以下抽象类:

public abstract class CloneableType{// Only derived types can support this// "polymorphic interface." Classes in other// hierarchies have no access to this abstract// member.public abstract object Clone();}

给定这个定义,只有扩展CloneableType的成员才能支持Clone()方法。如果您创建了一组不扩展此基类的新类,则无法获得此多态接口。此外,您可能还记得C#不支持类的多重继承。因此,如果你想创建一个MiniVan,它既是汽车又是克隆类型,你无法这样做:

// Nope! Multiple inheritance is not possible in C#// for classes.public class MiniVan : Car, CloneableType{}

正如你所猜到的,接口类型来救援。定义接口后,它可以由任何类或结构、任何层次结构、任何命名空间或任何程序集实现(用任何. NET编程语言编写)。如您所见,接口是高度多态的。考虑在System命名空间中定义的标准. NET接口,它的名称为IClonable接口定义了一个名为Clone()的方法:

public interface ICloneable{object Clone();}

由于你可能已经从专家那里获得了理论知识,我不会在这里重复所有这些,而是让我用一个简单的例子来解释我们可以使用/不能使用InterfaceAbstract class

假设你正在设计一个列出汽车所有功能的应用程序。在不同的点上,你需要继承,因为像DigitalFuelMeter、空调、座椅调节等一些属性对所有的汽车都是常见的。同样,我们只需要继承一些类,因为像制动系统(ABS、EBD)这样的一些属性仅适用于一些汽车。

下面的类作为所有汽车的基类:

public class Cars{public string DigitalFuelMeter(){return "I have DigitalFuelMeter";}
public string AirCondition(){return "I have AC";}
public string SeatAdjust(){return "I can Adjust seat";}}

假设我们为每辆车都有一个单独的类。

public class Alto : Cars{// Have all the features of Car class}
public class Verna : Cars{// Have all the features of Car class + Car need to inherit ABS as the Braking technology feature which is not in Cars}
public class Cruze : Cars{// Have all the features of Car class + Car need to inherit EBD as the Braking technology feature which is not in Cars}

假设我们需要一种方法来继承Verna和Cruze汽车的制动技术(不适用于Alto)。虽然两者都使用制动技术,但“技术”是不同的。因此,我们创建了一个抽象类,其中该方法将被声明为抽象,并应在其子类中实现。

public abstract class Brake{public abstract string GetBrakeTechnology();}

现在我们尝试从这个抽象类继承,制动系统的类型在Verna和Cruze中实现:

public class Verna : Cars,Brake{public override string GetBrakeTechnology(){return "I use ABS system for braking";}}
public class Cruze : Cars,Brake{public override string GetBrakeTechnology(){return "I use EBD system for braking";}}

看到上面两个类的问题了吗?它们继承了多个C#. Net不允许的类,即使该方法在子类中实现。这里需要接口。

interface IBrakeTechnology{string GetBrakeTechnology();}

实现如下:

public class Verna : Cars, IBrakeTechnology{public string GetBrakeTechnology(){return "I use ABS system for braking";}}
public class Cruze : Cars, IBrakeTechnology{public string GetBrakeTechnology(){return "I use EBD system for braking";}}

现在Verna和Cruze可以在Interface的帮助下使用自己的制动技术实现多重继承。

我想他们不喜欢你的回答,因为你给出的是技术上的差异,而不是设计上的差异。这个问题对我来说就像一个巨魔问题。事实上,接口和抽象类有完全不同的性质,所以你不能真正比较它们。我会告诉你我对接口的作用和抽象类的作用的看法。

接口说明:用于确保类之间的契约和低耦合,以便拥有更可维护、可扩展和可测试的应用程序。

抽象类:仅用于在具有相同响应性的类之间分解一些代码。请注意,这是多重继承在OOP中不是一件好事的主要原因,因为类不应该承担很多责任(使用组成代替)。

因此,接口具有真正的架构角色,而抽象类几乎只是实现的一个细节(当然,如果您正确使用它)。

如果你认为java作为OOP语言来回答这个问题,Java8版本会导致上面答案中的一些内容过时。现在java接口可以有具体实现的默认方法。

Oracle网站提供了interfaceabstract类之间的关键区别。

考虑使用抽象类如果:

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

考虑使用接口如果:

  1. 你期望不相关的类会实现你的接口。例如,许多不相关的对象可以实现Serializable接口。
  2. 您希望指定特定数据类型的行为,但不关心谁实现其行为。
  3. 您想利用类型的多重继承。

简单来说,我想用

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

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

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

当然,理解OOP中接口和抽象类的行为(以及语言如何处理它们)很重要,但我认为理解每个术语的确切含义也很重要。你能想象if命令不能完全按照术语的含义工作吗?此外,实际上,一些语言正在缩小接口和抽象之间的差异……如果偶然有一天这两个术语的操作几乎相同,至少你可以定义自己应该在哪里(以及为什么)使用它们。

如果你通读一些字典和其他字体,你可能会发现同一个术语有不同的含义,但有一些共同的定义。我认为我在本网站中找到的这两个含义非常非常好,非常合适。

接口说明

使分离的有时不相容的元素能够有效协调的事物或环境。

摘要:

本质本身集中了任何更广泛或更一般的事物或几种事物的基本品质的东西;本质。

示例:

你买了一辆车,它需要燃料。

在此处输入图片描述

你的汽车模型是XYZ,这是类型ABC,所以它是一辆具体的汽车,一辆汽车的特定实例。汽车不是一个真正的对象。事实上,它是一组抽象的标准(质量)来创建一个特定的对象。简而言之,汽车是抽象类,它是“某种东西,它本身集中了任何更广泛或更一般的东西的基本品质。

应该使用唯一符合汽车手册规格的燃料来填充汽车油箱。实际上,没有什么可以限制您添加任何燃料,但发动机只有使用指定的燃料才能正常工作,因此最好遵循其要求。要求说它接受与其他同类汽车ABC一样的标准燃料。

在面向对象的视图中,类型ABC的燃料不应该被声明为一个类,因为没有特定类型的汽车的具体燃料。尽管您的汽车可以接受抽象类Fuel或车辆燃料,但您必须记住,您只有一些现有的车辆燃料符合规范,即实现汽车手册中要求的燃料。简而言之,他们应该实现接口ABCGenreFuel,其中“……使分离的、有时不相容的元素能够有效地协调”

增编

此外,我认为你应该记住术语类的含义,这是(来自前面提到的同一网站):

班级:

种类:由于共同的属性、特征、品质或特征而被认为形成一个群体的许多人或事物;

这样,一个类(或抽象类)不应该只表示公共属性(比如接口),而是某种具有公共属性的组。接口不需要表示一种。它必须表示公共属性。这样,我认为类和抽象类可以用来表示不应该经常改变其方面的事物,就像人类是哺乳动物一样,因为它代表了一些种类。种类不应该经常改变自己。

除了比较Abstract classInterface,比较Abstract classConcrete class是有意义的。

使用抽象类,就像你在不扩展的情况下使用除了不想/无法实例化的具体类一样。并使你不想/无法实现的方法成为抽象方法。

如果您喜欢类比,请将chromium视为抽象类(您不能将其用作浏览器,因此无法实例化),chrome和Opera是源自chromium的具体类,浏览器附加结构是接口。

抽象类处理有效地打包类功能,而接口用于意图/契约/通信,并且应该与其他类/模块共享。

同时使用抽象类作为契约和(部分)契约实现者违反了SRP。使用抽象类作为契约(依赖项)限制了创建多个抽象类以获得更好的可重用性。

在下面的示例中,使用抽象类作为OrderManager的合同会产生问题,因为我们有两种不同的处理订单的方式——基于客户类型和类别(客户可以是直接的或间接的,也可以是金的或银的)。因此,接口用于合同,抽象类用于不同的工作流实施

public interface IOrderProcessor{bool Process(string orderNumber);}
public abstract class CustomerTypeOrderProcessor: IOrderProcessor{public bool Process(string orderNumber) => IsValid(orderNumber) ? ProcessOrder(orderNumber) : false;
protected abstract bool ProcessOrder(string orderNumber);
protected abstract bool IsValid(string orderNumber);}
public class DirectCustomerOrderProcessor : CustomerTypeOrderProcessor{protected override bool IsValid(string orderNumber) => string.IsNullOrEmpty(orderNumber);
protected override bool ProcessOrder(string orderNumber) => true;}
public class InDirectCustomerOrderProcessor : CustomerTypeOrderProcessor{protected override bool IsValid(string orderNumber) => orderNumber.StartsWith("EX");
protected override bool ProcessOrder(string orderNumber) => true;}
public abstract class CustomerCategoryOrderProcessor : IOrderProcessor{public bool Process(string orderNumber) => ProcessOrder(GetDiscountPercentile(orderNumber), orderNumber);
protected abstract int GetDiscountPercentile(string orderNumber);
protected abstract bool ProcessOrder(int discount, string orderNumber);}
public class GoldCustomer : CustomerCategoryOrderProcessor{protected override int GetDiscountPercentile(string orderNumber) => 15;
protected override bool ProcessOrder(int discount, string orderNumber) => true;
}
public class SilverCustomer : CustomerCategoryOrderProcessor{protected override int GetDiscountPercentile(string orderNumber) => 10;
protected override bool ProcessOrder(int discount, string orderNumber) => true;
}
public class OrderManager{private readonly IOrderProcessor _orderProcessor;// Not CustomerTypeOrderProcessor or CustomerCategoryOrderProcessor
//Using abstract class here would create problem as we have two different abstract classespublic OrderManager(IOrderProcessor orderProcessor) => _orderProcessor = orderProcessor;}

接口说明

不包含请求接口可以从多个接口继承(支持多重继承)成员自动公开可能包含属性、方法、事件和索引器

抽象类:

May/mayn不包含实现;至少有一个成员不会实现。类可以从单个Base类继承;不允许多重继承。成员有访问修饰符可能包含字段、属性、构造函数、析构函数、方法、事件和索引器

这些是抽象类和接口之间的主要区别。

这些答案都太长了。

  • 接口用于定义行为。

  • 抽象类是用来定义事物本身的,包括它的行为。这就是为什么我们有时会创建一个抽象类,其中包含一些继承接口的额外属性。

这也解释了为什么Java只支持类的单继承,而对接口没有限制。因为一个具体的对象不能是不同的东西,但它可以有不同的行为。

tl; dr;当你看到“Is A”关系时,使用继承/抽象类。当你看到“有”关系时,创建成员变量。当你看到“依赖于外部供应商”时,实现(而不是继承)一个接口。

面试问题:接口和抽象类有什么区别?你如何决定何时使用什么?我主要得到以下一个或全部答案:

ZK(这是我名字的首字母缩写):你不能创建两者的对象。所以这不是区别。这是接口和抽象类之间的相似之处。反问:为什么你不能创建抽象类或接口的对象?

答案2:抽象类可以有一个函数体作为部分/默认实现。

ZK:反问题:所以如果我把它改成一个纯抽象类,把所有的虚函数都标记为抽象的,并且没有为任何虚函数提供默认实现。这会让抽象类和接口变得一样吗?之后它们可以互换使用吗?

答案3:接口允许多继承,抽象类不允许。

ZK:反问题:你真的是从接口继承的吗?还是你只是实现一个接口并且从抽象类继承?实现和继承有什么区别?这些反问题让候选人感到困惑,让大多数人挠头或只是进入下一个问题。这让我认为人们需要帮助解决这些面向对象编程的基本构建块。最初问题和所有反问题的答案都在英语和UML中找到。你必须至少了解以下内容才能更好地理解这两个结构。

普通名词:普通名词是指同一类或同一种类的事物的“共同”名称。例如水果、动物、城市、汽车等。

专有名词:专有名词是指一个物体、地方或事物的名称。苹果、猫、纽约、本田雅阁等。

Car是一个普通名词,而Honda雅阁是一个专有名词,可能是一个复合专有名词,一个由两个名词组成的专有名词。

来到UML部分。您应该熟悉以下关系:

  • 是一个
  • 有一个
  • 使用

让我们考虑下面的两句话。-本田雅阁是汽车吗?-本田雅阁有车吗?

哪一个听起来正确?简单的英语和理解。本田雅阁和汽车有一个“是A”的关系。本田雅阁里面没有车。它“是”一辆车。本田雅阁里面有一个“音乐播放器”。

当两个实体共享“Is A”关系时,它是继承的更好候选者。并且有一个关系是创建成员变量的更好候选者。建立了这个关系,我们的代码看起来像这样:

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

而英文会这样读“本田雅阁是一款支持充气轮胎和加油的汽车”。