接口与抽象类(通用OO)

Hou*_*man 1372 oop abstract-class interface

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

根据我的经验,我认为以下是正确的.如果我错过了重点,请告诉我.

接口:

在接口中声明的每个单独的方法都必须在子类中实现.接口中只能存在事件,代理,属性(C#)和方法.一个类可以实现多个接口.

抽象类:

只有抽象方法必须由子类实现.Abstract类可以有实现的常规方法.Abstract类还可以在Events,Delegates,Properties和Methods旁边有类变量.由于C#中不存在多重继承,因此类只能实现一个抽象类.

  1. 毕竟,面试官想出了一个问题"如果你有一个只有抽象方法的抽象类怎么办?那么它与界面会有什么不同?" 我不知道答案,但我认为这是上面提到的继承权吗?

  2. 另一位采访者问我,如果你在界面中有一个Public变量,那么它与Abstract Class有什么不同?我坚持认为你不能在界面中有一个公共变量.我不知道他想听到什么,但他也不满意.

另见:

Jay*_*Jay 843

怎么样比喻:当我在空军时,我去了飞行员训练,并成为美国空军(美国空军)的飞行员.那时我没有资格飞行任何东西,不得不参加飞机类训练.一旦我获得资格,我就是一名飞行员(抽象班)和一名C-141飞行员(具体班级).在我的一项任务中,我获得了额外的职责:安全官.现在我仍然是一名飞行员和一名C-141飞行员,但我也履行了安全官职责(我实施了ISafetyOfficer,可以这么说).一名飞行员不需要担任安全官员,其他人也可以这样做.

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

  • 飞行员:抽象课
  • C-141飞行员:具体课程
  • 安全官员:界面

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

  • 我真的很喜欢这个比喻,它用一个简单的例子来解释一个稍微复杂的话题 (87认同)
  • 我还是有点困惑.比如说,你现在获得了F-16和T-38资格,所以现在班级`Jay`不能从多个班级(C-141飞行员,F-16飞行员和T-38飞行员)继承,这是否意味着谁的班级应该成为接口?谢谢 (54认同)
  • 很多人都正确地给出了Alex的评论+1,因为它揭示了这个例子中的一些弱点.首先,我会说杰伊将是C-141Pilot的一个实例,而不是它自己的类.此外,由于在美国空军中,99%的飞行员一次只能在一架飞机上获得资格(FCF和试飞员是明显的例外)我没有考虑多种资格以及如何实施.据我所知,50年前的飞行员同时获得了25架不同飞机的资格,我认为这说明了我们不想使用多重继承. (35认同)
  • 由于一名飞行员不太可能一次飞越多架飞机,因此这将是实施战略模式的好机会.Pilot将拥有一组认证,并在运行时选择正确的认证.通过TakeOff,Land,Eject方法,认证将被编码为实现IFlyPlane接口的行为. (18认同)
  • 这是理解复杂OO术语的最佳方式.简而言之,只有当你能够实际使用它时,所有理论才有价值.@Jay你rexample非常容易掌握然后几个子弹点(大多是穿透思维而不是被吸收!) (13认同)
  • @AlexOkrushko这是我的看法.飞行员*是一个*人.飞行员*可以做很多事情,比如说话(可玩),画画(抽奖)等等.飞行员*有很多资格.一个*is-a*关系意味着一个子类.A*can-do*关系意味着接口.并且a*has-a*关系意味着聚合 - 也就是说,飞行员的属性是他的资格列表.但现实生活并不总是遵循我们的规则. (8认同)
  • 在C++中,可以使用多重继承,尽管许多人会建议聚合而不是继承.http://stackoverflow.com/questions/269496/inheritance-vs-aggregation (2认同)
  • @sdasdadas我认为您不希望为资格的每次变更编写新的代码,即F16pilot类,F15pilot类,F15F16pilot类,C17F16pilot类,C17F15pilot类,C17F15F16pilot类等.相反,让Pilot类保持一个各种资格列表(即List <IQualification> GetQualifications();) (2认同)

Mic*_*urr 722

虽然你的问题表明它是"普通OO",但它似乎真正关注.NET对这些术语的使用.

在.NET中(类似于Java):

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

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

实际上,有一种称为非虚拟接口(NVI)的C++习惯用法,其中公共方法是非虚拟方法,可以"窃取"私有虚拟方法:

  • +1:我愿意打赌那些主持面试的猴子甚至没有意识到其他语言以不同方式实现OO. (79认同)
  • 请注意,在Java 8中,您现在可以在接口中使用默认方法和静态方法,这意味着Java接口可以具有实现.参考[这里](http://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html).显然你主要提到了.NET,所以这只是一个关于Java的观察. (18认同)
  • 谢谢.我想,既然你的回答提到了状态+对所有休息的一个很好的概述,我将你的回答标记为最终答案.你是对的我要求一般的OO,因为我的第一个面试官要求一般OO,但由于我是一个C#家伙,我倾向于忘记这一点.;-)同样感谢C++的解释,因为c ++总是令人兴奋. (7认同)
  • 我认为Michael提供的解释中的一个关键点是,在实现接口时,您必须实现接口中的所有成员,但是当从抽象类继承时,子类不需要实现其父成员 (6认同)
  • @JL我看不出问题出在哪里.你似乎把抽象方法与抽象类混淆了.Abstract*methods*没有实现.但是,在abstract*class*中,*some*方法可以是抽象的(即没有实现),而其他一些方法确实可以实现. (2认同)
  • 我知道这是旧的,但是在 C# 中接口和抽象类之间的一个非常重要的区别是,一旦你发布了一个接口,改变接口就是一个破坏性的变化,但对于抽象类来说则不然。 (2认同)

小智 216

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

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

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

例如.一辆汽车和一辆卡车分享了很多核心属性和汽车抽象类的行为,但他们也分享一些外围行为像生成废气甚至非汽车类,如钻孔机或PowerGenerators份额,并不一定限定汽车或卡车,所以Car,Truck,Driller和PowerGenerator都可以共享IExhaust相同的界面.

  • 我认为更好的比喻是"usesFuel",它会显示界面的*契约*性质. (32认同)

Dha*_*jay 193

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

接口用于2个相似/不相似的类之间的通信,它们不关心实现接口的类的类型(例如,Height可以是接口属性,它可以由Human,Building,Tree实现.如果你可以吃就没关系,你可以游泳,你可以死或任何东西..重要的只是你需要有高度(在你的课堂实施)).

  • 我真的很喜欢这个答案,因为通过查看更抽象的东西(例如_intent_)而不是仅仅_structure_来回答事物之间的"什么"是不同的(因为结构上,接口和纯抽象类几乎是相同的)事情). (7认同)
  • @dhananjay:我看到身高可以与动物类的概念区分开,也可以与另一个不同的类分开,但是,类之间的“交流”到底是什么意思?只是为自己的班级定义了高度,对吗? (2认同)

Ree*_*sey 77

还有其他一些差异 -

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

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

可以在v2 +中修改抽象基类,而不会破坏API.对接口的更改正在破坏更改.

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

  • + 1表示可以在类上实现多个接口的关键点. (4认同)

fz_*_*lam 67

继承
考虑一辆汽车和一辆公共汽车.它们是两种不同的载体.但是,他们仍然有一些共同的属性,比如他们有转向,刹车,齿轮,引擎等.
所以继承概念,这可以表示如下......

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 ...
}
Run Code Online (Sandbox Code Playgroud)

现在是自行车......

public class Bicycle extends Vehicle {
    //You define properties which are unique to bicycles here ...
    private Pedal pedal;
}
Run Code Online (Sandbox Code Playgroud)

还有一辆车......

public class Car extends Vehicle {
    private Engine engine;
    private Door[] doors;
}
Run Code Online (Sandbox Code Playgroud)

这就是继承.我们使用它们将对象分类为更简单的Base形式及其子代,如上所示.

抽象类

抽象类是不完整的对象.为了进一步理解,让我们再次考虑车辆类比.
可以驾驶车辆.对?但是不同的车辆以不同的方式驾驶......例如,你驾驶自行车时不能驾驶汽车.
那么如何表示车辆的驱动功能呢?更难以检查它是什么类型的车辆并用它自己的功能驱动它; 添加新型车辆时,您必须反复更改Driver类.
这里有抽象类和方法的作用.您可以将驱动器方法定义为抽象,以告知每个继承子项必须实现此函数.
所以,如果你修改车辆类......

//......Code of Vehicle Class
abstract public void drive();
//.....Code continues
Run Code Online (Sandbox Code Playgroud)

自行车和汽车还必须指定如何驾驶它.否则,代码将无法编译并引发错误.
简而言之,抽象类是一个部分不完整的类,其中包含一些不完整的函数,继承的子函数必须指定它们自己的函数.

接口 接口完全不完整.他们没有任何财产.他们只是表明继承的孩子能够做某事......
假设你有不同类型的手机.他们每个人都有不同的方式来做不同的功能; 例如:打电话给一个人.手机制造商指定了如何做到这一点.在这里,手机可以拨打一个号码 - 也就是说,它是可拨号的.让我们将其表示为一个界面.

public interface Dialable {
    public void dial(Number n);
}
Run Code Online (Sandbox Code Playgroud)

这里Dialable的制造商定义了如何拨打号码.您只需要拨打一个号码即可拨号.

// 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);
}
.....
Run Code Online (Sandbox Code Playgroud)

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

更重要的是,如果有一天你用不同的方式切换Dialable

......
public static void main(String[] args) {
    Dialable myDialable = SomeLibrary.PHONE2; // <-- changed from PHONE1 to PHONE2
    SomeOtherLibrary.doSomethingUsingADialable(myDialable);
}
.....
Run Code Online (Sandbox Code Playgroud)

您可以确定代码仍然可以正常工作,因为使用dialable的函数不会(也不能)依赖于Dialable接口中指定的细节以外的细节.它们都实现了Dialable接口,这是函数唯一关心的东西.

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

更多信息 抽象类与接口


Rav*_*abu 40

如果您考虑java使用OOP语言来回答这个问题,那么Java 8版本会使上述答案中的某些内容过时.现在java接口可以有默认方法和具体实现.

Oracle 网站提供了类interfaceabstract类之间的主要区别.

考虑使用抽象类,如果:

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

考虑使用接口:

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

简单来说,我想用

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

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

看看代码示例以清楚的方式理解事物:我应该如何解释Interface和Abstract类之间的区别?


Ste*_*owe 32

采访者正在咆哮着一棵奇怪的树.对于像C#和Java这样的语言,有区别,但在其他语言中,如C++则没有.OO理论并没有区分这两者,仅仅是语言的语法.

抽象类是一个包含将继承的实现和接口(纯虚方法)的类.接口通常没有任何实现,只有纯虚函数.

在C#或Java中,没有任何实现的抽象类与仅从用于从其继承的语法的接口不同,并且事实上您只能从一个继承.


The*_*TXI 31

通过实现接口,您实现了组合("has-a"关系)而不是继承("is-a"关系).这是一个重要的原则,要记住在涉及设计模式的事情时,您需要使用接口来实现行为的组合而不是继承.

  • 接口实现了IMO,更多的是"Acts-as-a"关系.封装比界面更好地实现了组合. (17认同)
  • 我不认为实现接口会受到影响. (12认同)

bja*_*jan 26

从概念上讲,通过使用任何一个或两个来保持语言特定的实现,规则,好处和实现任何编程目标,可以或不能拥有代码/数据/属性,等等,单个或多个继承,全部放在一边

1-抽象(或纯抽象)类旨在实现层次结构.如果您的业务对象在结构上看起来有点相似,那么只表示父子(层次结构)类关系,那么将使用继承/抽象类.如果您的业务模型没有层次结构,则不应使用继承(这里我不是在讨论编程逻辑,例如某些设计模式需要继承).从概念上讲,抽象类是一种在OOP中实现业务模型层次结构的方法,它与Interfaces无关,实际上将Abstract类与Interface进行比较是没有意义的,因为两者在概念上完全不同,在访谈中只是要检查概念因为它看起来在实现时都提供了相同的功能,我们程序员通常更多地强调编码.[请记住,抽象与抽象类不同].

2-接口是合同,是由一组或多组功能表示的完整业务功能.这就是为什么它被实现而不是继承.业务对象(层次结构的一部分或非层次结构)可以具有任意数量的完整业务功能.它与抽象类无关,一般意味着继承.例如,人类可以运行,大象可以运行,鸟可以运行,等等,所有这些不同层次的对象都将实现RUN接口或EAT或SPEAK接口.不要进入实现,因为您可能将其实现为具有实现这些接口的每种类型的抽象类.任何层次结构的对象都可以具有与其层次结构无关的功能(接口).

我相信,Interfaces并不是为了实现多重继承或暴露公共行为而发明的,类似地,纯抽象类不是否决于接口,而Interface是一个对象可以做的功能(通过该接口的功能)和Abstract Class代表一个生成具有父级核心结构(属性+功能)的子级的层次结构的父级

当您被问及差异时,除非明确询问,否则实际上是概念差异而不是语言特定实现的差异.

我相信,两位采访者都期待这两者之间存在直线上的差异,当你失败时,他们试图通过实施ONE作为其他方式来驱使你走向这种差异.

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


Jeg*_*ala 26

我将解释接口和抽象类的深度细节.如果您了解接口和抽象类的概述,那么当我们应该使用接口时以及何时应该使用抽象类时,第一个问题会出现在您的脑海中.所以请查看以下接口和抽象类的说明.

  1. 什么时候应该使用Interface?

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

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

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

    接口

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

    抽象

    可以有Concrete方法和抽象方法,什么是具体方法,在Abstract类中有实现,抽象类是一个被声明为abstract的类 - 它可能包含也可能不包含抽象方法.

    接口

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

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

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

    接口方法
    我们也不能声明接口为private,protected,final,static,synchronized,native .....

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

    抽象

    我们很高兴地使用公共的,私人的最终静态....意味着没有限制适用于抽象.

    接口

    变量在接口中声明为默认的公共静态final,因此我们也不会将变量声明为private,protected.

    易失性修饰符也不适用于接口,因为接口变量默认为public static final和final变量,一旦将值赋值给变量就不能更改值,一旦将变量声明为接口,就必须分配变量.

    而且变量是变化的,所以它就是对手.最终这就是我们不在界面中使用volatile变量的原因.

    抽象

    抽象变量不需要声明public static final.

我希望这篇文章很有用.

  • 我不同意这一点:`抽象类必须至少有一个抽象方法.只要你实现它,就可以有一个没有Abstract方法的Abstract类.参考:`抽象类是一个被声明为抽象的类 - 它可能包括也可能不包括抽象方法.参考文献来源:http://docs.oracle.com/javase/tutorial/java/IandI/abstract.html (4认同)

Cha*_*ana 21

对于.Net,

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

编辑:另一方面,我甚至不会使用短语"子类"(或"继承"短语)来描述"定义为实现"接口的类.对我来说,接口是一个合同的定义,如果一个类被定义为"实现"该接口,那么该类必须符合该合同.它不会继承任何东西......你必须自己添加一切,明确地.

  • 不仅仅是状态....抽象类可以有IMPLEMENTATION.也就是说,他们可以在其中使用实际运行并执行某些操作的代码的方法,这些方法由基类的实例进行处理和执行...不是这样的接口 (4认同)
  • 是! 州!这就是第二个采访者用他奇怪的方式在界面中说"公共变量"的意思.天哪!抽象类可以有状态,接口不行!是的,其他人也同意他们的继承方式之间的差异,我忘记提及但后来已经弄清楚了.:) 感谢大家! (2认同)

K.M*_*iao 19

这些答案都太长了。

  • 接口用于定义行为。

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

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


bou*_*ter 18

接口:如果您想要对可能相互关联或不相关的组件施加规则,则应使用此接口

优点:

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

缺点:

  1. 必须实施所有定义的合同
  2. 不能有变量或代表
  3. 一旦定义,就不能在不破坏所有类的情况下进行更改

抽象类:应该用于您希望对彼此相关的组件具有某些基本或默认行为或实现的位置

优点:

  1. 比界面更快
  2. 具有实施的灵活性(您可以完全或部分实施)
  3. 可以轻松更改而不会破坏派生类

缺点:

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


Gnu*_*cki 17

我认为他们不喜欢你的回答,因为你给出了技术差异而不是设计差异.这个问题对我来说就像一个巨魔问题.事实上,接口和抽象类具有完全不同的性质,因此您无法真正比​​较它们.我将向您展示我对接口的作用是什么以及抽象类的作用是什么.

interface:用于确保合同并在类之间建立低耦合,以便具有更易维护,可伸缩和可测试的应用程序.

abstract class:仅用于对具有相同责任性的类之间的某些代码进行分解.请注意,这是多重继承在OOP中是一件坏事的主要原因,因为类不应该处理许多响应(而是使用组合).

所以接口具有真正的架构角色,而抽象类几乎只是实现的细节(如果你当然正确使用它).


Ani*_*kur 13

After all that, the interviewer came up with the question "What if you had an 
Abstract class with only abstract methods? How would that be different
from an interface?" 
Run Code Online (Sandbox Code Playgroud)

文档明确指出,如果抽象类只包含抽象方法声明,则应将其声明为接口.

An another interviewer asked me what if you had a Public variable inside
the interface, how would that be different than in Abstract Class?
Run Code Online (Sandbox Code Playgroud)

接口中的变量默认为public static和final.如果抽象类中的所有变量都是公共的,那么问题就可以被设置为框架?好吧,与接口中的变量不同,它们仍然可以是非静态的,非最终的.

最后,我要再添加一点上面提到的那些 - 抽象类仍然是类并且属于单个继承树,而接口可以存在于多个继承中.


nau*_*svn 12

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

这是我的意见.


Dee*_*hra 12

由Jeffrey Richter通过C#复制CLR ......

我经常听到这样一个问题,"我应该设计一个基本类型还是一个界面?"答案并不总是明确的.

以下是一些可能对您有所帮助的准则:

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

易于使用作为开发人员,您通常更容易定义从基本类型派生的新类型,而不是实现接口的所有方法.基类型可以提供许多功能,因此派生类型可能只需要对其行为进行相对较小的修改.如果提供接口,则新类型必须实现所有成员.

■■一致的实施无论接口合同的记录情况如何,每个人都不太可能100%正确地实施合同.事实上,COM遇到了这个问题,这就是为什么有些COM对象只能与Microsoft Word或Windows Internet Explorer一起正常工作的原因.通过提供具有良好默认实现的基类型,您可以开始使用有效且经过充分测试的类型; 然后,您可以修改需要修改的部件.

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


joe*_*dev 10

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

抽象类为其子阶段定义基础结构,并可选择部分实现.抽象类以垂直但有方向的方式提供多态性,因为任何继承抽象类的类都可以被视为该抽象类的实例,而不是相反.抽象类可以并且经常包含实现细节,但不能自己实例化 - 只有它们的子类可以"新建".

C#确实允许接口继承,请注意.


mcv*_*mcv 10

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

一个接口是一个协议.它规定:"这就是我们如何相互交谈".它不能有任何实现,因为它不应该有任何实现.这是一份合同.它就像.hC中的头文件.

一个抽象类是一个不完整的实现.类可能实现也可能不实现接口,而抽象类不必完全实现它.没有任何实现的抽象类是没用的,但完全合法.

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

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


Sar*_*avu 10

正如你可能已经得到了有关专家的理论知识,我不是在这里重复所有那些花很多话,而让我用一个简单的例子,我们可以使用/不能使用说明InterfaceAbstract class.

考虑一下您正在设计一个列出Cars的所有功能的应用程序.在各个方面,您需要共同继承,因为DigitalFuelMeter,Air Conditioning,Seat adjustment等一些属性对于所有汽车来说都很常见.同样,我们只需要某些类的继承,因为制动系统(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";
    }
}
Run Code Online (Sandbox Code Playgroud)

考虑我们为每辆车都有一个单独的课程.

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        
}
Run Code Online (Sandbox Code Playgroud)

考虑一下我们需要一种方法来继承Verna和Cruze汽车的制动技术(不适用于Alto).虽然两者都使用制动技术,但"技术"却不同.所以我们创建了一个抽象类,其中方法将被声明为Abstract,它应该在其子类中实现.

public abstract class Brake
{
    public abstract string GetBrakeTechnology();
}
Run Code Online (Sandbox Code Playgroud)

现在我们试图继承这个抽象类,并且在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";
    }         
}
Run Code Online (Sandbox Code Playgroud)

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

interface IBrakeTechnology
{
    string GetBrakeTechnology();
}
Run Code Online (Sandbox Code Playgroud)

实现如下:

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";
   }        
}
Run Code Online (Sandbox Code Playgroud)

现在Verna和Cruze可以在Interface的帮助下通过自己的制动技术实现多重继承.

  • 由于这些例子,这是最好的解释之一. (4认同)
  • 这对我来说是有意义的,而不会让大脑受伤.我只是想为我的学生提出一个汽车示例.感谢你把时间放在一起. (2认同)

fas*_*ava 9

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


MRF*_*ius 7

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

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

例如:

  public interface IVariable
  {
      string name {get; set;}
  }
Run Code Online (Sandbox Code Playgroud)

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


par*_*agy 7

虽然这个问题已经很老了,但我想补充一点赞成接口:

可以使用任何依赖注入工具注入接口,而极少数支持抽象类注入.


小智 6

回答第二个问题:public在定义的变量interfacestatic final通过默认而public在变量abstract类的实例变量.


Jah*_*han 6

接口类型与抽象基类

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

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

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();
}
Run Code Online (Sandbox Code Playgroud)

根据此定义,只有扩展CloneableType的成员才能支持Clone()方法.如果您创建一组不扩展此基类的新类,则无法获得此多态接口.此外,您可能还记得C#不支持类的多重继承.因此,如果您想创建一个CarVan并且是CloneableType的MiniVan,则无法执行此操作:

// Nope! Multiple inheritance is not possible in C#
// for classes.
public class MiniVan : Car, CloneableType
{
}
Run Code Online (Sandbox Code Playgroud)

正如您所猜测的那样,接口类型可以解决问题.定义接口后,它可以由任何类或结构,任何层次结构,任何命名空间或任何程序集(用任何.NET编程语言编写)实现.如您所见,接口具有高度多态性.考虑在System命名空间中定义的名为ICloneable的标准.NET接口.此接口定义了一个名为Clone()的方法:

public interface ICloneable
{
object Clone();
}
Run Code Online (Sandbox Code Playgroud)


Fel*_*ira 6

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

如果您通读一些词典和其他字体,可能会发现同一术语具有不同的含义,但具有一些共同的定义。我认为我在本站点中发现的这两种含义非常非常好并且很合适。

接口:

一种事物或情况,可以使单独的,有时是不兼容的元素有效地进行协调。

抽象:

某种东西本身集中于任何更广泛或更普遍的东西或几件事的本质素质;本质。

例:

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

在此处输入图片说明

您的汽车模型为XYZ,属于类型ABC,因此它是混凝土汽车,是汽车的特定实例。汽车不是真正的物体。实际上,它是创建特定对象的一组抽象标准(质量)。简而言之,Car是一个抽象类,它是“某种东西本身就集中了任何更广泛或更普遍的东西的本质素质”

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

在面向对象的观点中,ABC不应将用于体裁的燃料声明为类别,因为那里没有用于特定类型汽车的混凝土燃料。尽管您的汽车可以接受抽象类的Fuel(燃料)或VehicularFuel(车辆燃料),但您必须记住,您现有的仅有部分车辆燃料符合规范,即满足您汽车手册中要求的规范。简而言之,他们应该实现接口 ABCGenreFuel,该接口“ ...使单独的,有时是不兼容的元素能够有效地进行协调”

附录

另外,我认为您应该牢记“类”一词的含义,即(来自前面提到的同一站点):

类:

由于共同的属性,特征,品质或特质而被视为一个团体的许多人或事物;类;

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

  • 太多的废话,不要让人们听起来比现在更令人困惑。 (2认同)

Ada*_*der 5

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

根据我的经验,当你有几个类需要响应相同的方法或方法时,最好使用接口,以便它们可以被其他代码交替使用,这些代码将针对这些类的通用接口编写.接口的最佳用途是协议很重要,但每个类的底层逻辑可能不同.如果您将复制逻辑,请考虑抽象类或标准类继承.


Viv*_*ani 5

从编码的角度

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

从设计角度

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

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


Zia*_*han 5

tl;博士; 当您看到“是 A”关系时,请使用继承/抽象类。当您看到“具有”关系时,请创建成员变量。当您看到“依赖于外部提供者”时实现(而不是继承)一个接口。

面试题:接口和抽象类有什么区别?你如何决定何时使用什么?我大多得到以下一个或所有答案: 答案 1:您不能创建抽象类和接口的对象。

ZK(这是我的首字母缩写):您不能创建任何一个对象。所以这没有区别。这是接口和抽象类之间的相似之处。反问:为什么不能创建抽象类或接口的对象?

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

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

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

ZK:反问:你真的从接口继承吗?或者你只是实现一个接口并从抽象类继承?实现和继承有什么区别?这些反问问题让考生望而却步,让大多数人摸不着头脑,或者只是转到下一个问题。这让我认为人们需要有关面向对象编程的这些基本构建块的帮助。原始问题和所有反问题的答案都可以在英语和 UML 中找到。您必须至少了解以下内容才能更好地理解这两个结构。

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

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

汽车是一个普通名词。本田雅阁是一个专有名词,可能是一个复合专有名词,一个由两个名词组成的专有名词。

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

  • 是一个
  • 有一个
  • 用途

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

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

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

abstract class Car
{
   string color;
   int speed;
}
class HondaAccord : Car
{
   MusicPlayer musicPlayer;
}
Run Code Online (Sandbox Code Playgroud)

现在本田不生产音乐播放器。或者至少这不是他们的主要业务。

所以他们联系了其他公司并签订了合同。如果您在这里接收电源并且这两条线上的输出信号在这些扬声器上播放得很好。

这使得音乐播放器成为界面的完美候选者。只要连接工作正常,您并不关心谁为其提供支持。

您可以用 Sony 或其他方式替换 LG 的 MusicPlayer。它不会改变本田雅阁的任何事情。

为什么不能创建抽象类的对象?

因为你不能走进陈列室说给我一辆车。您必须提供专有名词。什么车?应该是本田雅阁。那时销售代理可以为您提供一些东西。

为什么不能创建接口的对象?因为你不能走进陈列室说给我一份音乐播放器的合同。它不会有帮助。消费者和提供者之间的接口只是为了促进达成协议。您将如何处理协议副本?它不会播放音乐。

为什么接口允许多重继承?

接口不是继承的。接口已实现。界面是与外部世界交互的候选者。本田雅阁有加油接口。它有给轮胎充气的接口。和用来给足球充气的软管是一样的。所以新代码如下所示:

abstract class Car
{
    string color;
    int speed;
}
class HondaAccord : Car, IInflateAir, IRefueling
{
    MusicPlayer musicPlayer;
}
Run Code Online (Sandbox Code Playgroud)

而英文会这样读:“Honda Accord is a Car that support the 充气轮胎和加油”。


归档时间:

查看次数:

688651 次

最近记录:

6 年,4 月 前