Oop 接口与抽象类(通用OO)

Oop 接口与抽象类(通用OO),oop,interface,abstract-class,Oop,Interface,Abstract Class,我最近接受了两次电话采访,有人问我接口和抽象类之间的区别。我已经解释了我能想到的每一个方面,但他们似乎在等我提到一些具体的东西,我不知道是什么 根据我的经验,我认为以下是正确的。如果我遗漏了一个要点,请告诉我 接口: 接口中声明的每个方法都必须在子类中实现。 接口中只能存在事件、委托、属性和方法。一个类可以实现多个接口 抽象类: 子类只能实现抽象方法。抽象类可以具有带有实现的普通方法。除了事件、委托、属性和方法之外,抽象类还可以有类变量。由于C语言中不存在多重继承,一个类只能实现一个抽象类 在所

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

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

接口:

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

抽象类:

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

在所有这些之后,面试官提出了一个问题:如果你有一个只有抽象方法的抽象类会怎么样?这与界面有什么不同?我不知道答案,但我认为这是上面提到的遗产,对吗

另一位面试官问我,如果在接口中有一个公共变量,这与抽象类中有什么不同?我坚持在接口中不能有公共变量。我不知道他想听什么,但他也不满意

另见:


还有一些其他的区别-

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

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

可以在v2+中修改抽象基类,而无需破坏API。对接口的更改是破坏性的更改

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

对于.Net

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


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

面试官们正在一棵奇怪的树上吠叫。对于C语言和java语言,有不同之处,但是在C++语言中没有。OO理论并没有区分这两者,仅仅是语言的语法

抽象类是一个同时具有实现和接口的类,纯虚拟方法将被继承。接口通常没有任何实现,只有纯虚拟函数

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

from,主要涉及何时使用一个或另一个:

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


1一个接口可以看作是一个纯抽象类,是相同的,但尽管如此,实现一个接口和继承一个抽象类并不相同。当您从这个纯抽象类继承时,您正在定义一个层次结构->继承,如果您实现的接口不是您自己,那么您可以实现任意多的接口,但是您只能从一个类继承

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

例如:

  public interface IVariable
  {
      string name {get; set;}
  }
clas
实现该接口的必须具有这样的属性。

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

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

在.NET中,与Java类似:

接口不能有状态或实现 实现接口的类必须提供该接口所有方法的实现 抽象类可以包含状态数据成员和/或实现方法 抽象类可以在不实现抽象方法的情况下继承,尽管这样的派生类本身是抽象的 接口可能是多继承的,抽象类可能不是。这可能是接口与抽象类分开存在的关键具体原因——它们允许实现多继承,从而消除了一般MI的许多问题。 作为一般的OO术语,这些差异不一定定义明确。例如,有C++程序员可能持有类似的刚性定义接口是抽象类的严格子集,不能包含实现,而有些人可能会说,具有一些默认实现的抽象类仍然是接口,或者非抽象类仍然可以定义接口。
public interface Dialable {
    public void dial(Number n);
}

确实有一个C++习语,称为非虚拟接口NVI,其中公共方法是非虚方法,它对私有虚拟方法“thouk”:


第二个问题的答案:接口中定义的公共变量默认为静态final,而抽象类中的公共变量是实例变量。

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

打个比方吧:当我在空军时,我参加了飞行员培训,成为了美国空军飞行员。在那一点上,我没有资格驾驶任何东西,必须参加飞机类型的训练。一旦我取得资格,我就是一名飞行员抽象班和一名C-141飞行员具体班。在我的一次任务中,我被赋予了一个额外的职责:安全官员。现在,我仍然是一名飞行员和一名C-141飞行员,但我也履行了安全员的职责,可以说,我执行了ISafetyOfficer。飞行员不需要是安全员,其他人也可以这样做

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

飞行员:抽象类 C-141飞行员:混凝土级 安全官员:接口
补充说明:这是一个用来帮助解释概念的类比,而不是一个编码建议。看到下面的各种评论,讨论很有趣。

我认为他们寻找的答案是基本的或OPP哲学上的差异

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

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

例如,汽车和卡车共享汽车抽象类的许多核心属性和行为,但它们也共享一些外围行为,如生成废气,即使是非汽车类(如钻机或发电机)也共享这些行为,不一定定义汽车或卡车,所以汽车、卡车、,司钻和发电机都可以共享同一个接口IExhaust

接口: 我们不实现或定义方法,而是在派生类中实现或定义方法。 我们不在接口中声明成员变量。 接口表示HAS-A关系。这意味着它们是对象的遮罩。 抽象类: 我们可以在抽象类中声明和定义方法。 我们隐藏了它的构造函数。这意味着没有直接从中创建对象。 抽象类可以保存成员变量。 派生类继承到抽象类,这意味着派生类中的对象不会被屏蔽,而是继承到抽象类。本例中的关系是-A。
这是我的观点。

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

1-抽象或纯抽象类旨在实现层次结构。如果您的业务对象在结构上看起来有些相似,仅表示父子层次结构类型的关系,那么将使用继承/抽象类。如果您的业务模型没有层次结构,那么继承就不应该存在 这里使用的不是编程逻辑,例如,一些设计模式需要继承。从概念上讲,抽象类是在OOP中实现业务模型层次结构的方法,它与接口无关,实际上比较抽象类和接口是没有意义的,因为两者在概念上完全不同,在访谈中,它被要求检查概念,因为在实现方面,它看起来都提供了一些相同的功能,我们程序员通常更强调编码。[请记住,抽象不同于抽象类]

2-接口是一个契约,由一组或多组功能表示的完整业务功能。这就是为什么它是实现的,而不是继承的。层次结构中的业务对象部分(无论是否)可以具有任意数量的完整业务功能。它与抽象类无关,通常意味着继承。例如,一个人可以跑,一头大象可以跑,一只鸟可以跑,等等,所有这些不同层次的对象都将实现RUN接口或EAT或SPEAK接口。不要进入实现,因为您可能会将其实现为每个类型都有抽象类来实现这些接口。任何层次结构的对象都可以具有与其层次结构无关的FunctionityInterface

我相信,界面的发明不是为了实现多重继承或公开公共行为,同样,纯抽象类不会否决接口,但接口是一种功能,对象可以通过该接口的函数来实现,抽象类表示层次结构的父级,以生成具有核心结构属性+父级功能的子级

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

我相信,两位面试官都期望这两者之间有一条直线的区别,当你失败时,他们试图通过将一条作为另一条来实现这一区别

如果有一个只包含抽象方法的抽象类呢


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


接口用于2个相似/非相似类之间的通信,这些类不关心实现interfaceee.g的类的类型。高度可以是接口属性,可以由人、建筑物、树来实现。你能不能吃,能不能游,能不能死都没关系。。重要的是你需要在你的课堂上实现高度

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

接口可以使用任何依赖项注入工具注入,其中抽象类注入由极少数继承支持。

考虑一辆小汽车和一辆公共汽车。它们是两种不同的交通工具。但是,它们仍然有一些共同的特性,比如它们有方向盘、制动器、齿轮、发动机等。 因此,使用继承概念,这可以表示为以下内容

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;
}
这都是关于继承的。我们使用它们将对象分类为更简单的基本形式及其子对象,正如上面所看到的

抽象类

抽象类是不完整的对象。为了进一步理解它,让我们再次考虑车辆类比。 可以驾驶车辆。正当但是不同的车辆以不同的方式驾驶。。。例如,你不能像开车一样开车。 那么,如何表示车辆的驱动功能呢?更难检查它是哪种类型的车辆,更难使用它自己的功能驾驶它;添加新车型时,您必须反复更改驾驶员等级。 下面是抽象类和方法的角色。您可以将drive方法定义为abstract,以说明每个继承子级都必须实现此函数。 因此,如果您修改车辆类别

//......Code of Vehicle Class
abstract 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);
}
.....
因此,使用接口而不是抽象类,使用可拨号函数的函数的编写者不必担心其属性。它有触摸屏或拨号板吗?是固定电话还是移动电话。你只需要知道它是否可拨;它是否实现了可拨号接口

public interface Dialable {
    public void dial(Number n);
}
更重要的是,如果有一天你换了一个不同的拨号

......
public static void main(String[] args) {
    Dialable myDialable = SomeLibrary.PHONE2; // <-- changed from PHONE1 to PHONE2
    SomeOtherLibrary.doSomethingUsingADialable(myDialable);
}
.....
您可以确保代码仍能正常工作,因为使用可拨号接口的函数不依赖于也不能依赖于可拨号接口中指定的细节以外的其他细节。它们都实现了一个可拨号接口,这是函数唯一关心的事情

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

更多信息

明确地说,如果抽象类只包含抽象方法声明,那么应该将其声明为接口

An another interviewer asked me what if you had a Public variable inside
the interface, how would that be different than in Abstract Class?
接口中的变量默认为公共静态变量和最终变量。这个问题的框架可能是,若抽象类中的所有变量都是公共的呢?它们仍然可以是非静态和非最终的,不像接口中的变量


最后,我想在上面提到的那些基础上再补充一点——抽象类仍然是类,并且落在一个继承树中,而接口可以存在于多个继承中。

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

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


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

从编码的角度来看

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

从设计角度

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

决定你需要什么:仅仅是策略实施,或者代码可重用性和策略。

大多数答案关注抽象类和接口之间的技术差异,但从技术上讲,接口基本上是一种抽象类,没有任何数据或实现,我认为概念上的差异更有趣,这可能就是面试官想要的

接口是一种协议。它明确规定:这就是我们彼此交谈的方式。它不能有任何实现,因为它不应该有任何实现。这是一份合同。这就像C中的.h头文件

抽象类是一个不完整的实现。类可以实现接口,也可以不实现接口,抽象类不必完全实现接口。没有任何实现的抽象类是无用的,但完全合法

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


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

由Jeffrey Richter通过C从CLR复制而来

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

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

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

public interface Dialable {
    public void dial(Number n);
}
■■ 易用性作为开发人员,通常更容易定义从 基类型,而不是实现接口的所有方法。基类型可以提供 很多功能,因此派生类型可能只需要对其行为进行相对较小的修改。如果提供接口,则新类型必须实现所有成员

■■ 一致的实现无论接口契约记录得多么好,它都是 很可能每个人都会百分之百地正确执行合同。事实上 遇到这个问题,这就是为什么一些COM对象只能在 微软 Word或Windows Internet Explorer。通过为基本类型提供良好的 在默认实现中,您首先使用一种有效且经过良好测试的类型;那么你可以 修改需要修改的零件

■■ 版本控制如果向基类型添加方法,则派生类型将继承新方法, 您首先使用一种有效的类型,用户的源代码甚至不需要重新编译。 向接口添加新成员将强制更改接口的继承者
它的源代码和重新编译。

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

什么时候应该使用界面

如果您不知道实现,我们有需求规范,那么我们就使用接口

什么时候应该使用抽象类

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

接口

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

摘要

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

接口

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

为什么我们不将接口声明为私有和受保护的

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

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

我会说明理由: 为什么我们不声明同步方法,因为我们无法创建接口的对象,并且同步是在对象上进行的,所以我们不声明同步方法的原因 瞬态概念也不适用,因为瞬态工作是同步的

摘要

我们很乐意使用公共、私人最终静态。。。。表示抽象上不适用任何限制

接口

变量在接口中被声明为默认的公共静态final,所以我们也不被声明为私有的、受保护的变量

Volatile修饰符也不适用于接口,因为接口变量在默认情况下是公共静态final和final变量。一旦将值赋给变量,则无法更改该值。一旦将变量声明给接口,则必须赋给该变量

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

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

抽象变量不需要声明为公共静态final

我希望这篇文章是有用的。

如果您想对组件暗示一条规则,那么应该使用Interface: 相互关联

优点:

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

必须执行所有定义的合同 不能有变量或委托 一旦定义,在不破坏所有类的情况下无法更改 抽象类:应该在您希望对彼此相关的组件具有一些基本或默认行为或实现的地方使用

优点:

比接口快 在实现方面具有灵活性—您可以完全或部分实现它 可以在不破坏派生类的情况下轻松更改 缺点:

无法实例化 不支持多重继承 接口类型与抽象基类

改编自这本书

接口类型可能与抽象基类非常相似。回忆起 当一个类被标记为抽象时,它可以定义任意数量的抽象成员来提供 所有派生类型的多态接口。但是,即使类确实定义了一组抽象 成员,它还可以自由定义任意数量的构造函数、字段数据、非抽象成员和 实施等等。另一方面,接口只包含抽象成员定义。 由抽象父类建立的多态接口有一个主要限制 因为只有派生类型支持抽象父级定义的成员。然而,在更大的范围内 在软件系统中,开发没有公共父级的多类层次结构是非常常见的 超越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();
}
根据此定义,只有扩展CloneableType的成员才能支持克隆 方法如果您创建了一组不扩展此基类的新类,则无法获得此值 多态接口。另外,您可能还记得C不支持类的多重继承。 因此,如果您想创建一辆小型货车,它是一辆汽车,也是一辆可克隆型,您无法做到:

// Nope! Multiple inheritance is not possible in C#
// for classes.
public class MiniVan : Car, CloneableType
{
}
正如您所猜测的,接口类型起到了解救作用。定义接口后,它可以 可以由任何类或结构、任何层次结构、任何命名空间或任何程序集实现 用任何.NET编程语言编写。正如您所看到的,接口是高度多态的。 考虑在系统命名空间中定义的标准.NET接口IcLoNeLet。这 接口定义了一个名为Clone的方法:

public interface ICloneable
{
object Clone();
}

由于您可能已经从专家那里获得了理论知识,因此我不会花太多的话来重复所有这些内容,而是让我用一个简单的示例来解释我们可以使用/不能使用接口和抽象类的地方

假设您正在设计一个应用程序来列出汽车的所有功能。在不同的方面,您需要共同继承,因为数字燃油表、空调、座椅调节等特性在所有汽车中都很常见。同样,我们只需要继承某些类,因为制动系统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        
}
考虑到我们需要一种方法来继承不适用于Alto的Verna和Cruze汽车的制动技术。虽然两者都使用制动技术,但技术不同。因此,我们正在创建一个抽象类,其中该方法将被声明为抽象的,并且应该在其子类中实现

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 IBrakeTechnology
{
    string GetBrakeTechnology();
}
接口:用于确保类之间的契约和低耦合,以便有一个更可维护、可扩展和可测试的应用程序

抽象类:仅用于在具有相同职责的类之间分解某些代码。请注意,这就是为什么多重继承在OOP中是一件坏事的主要原因,因为它是一种使用

<> P>接口具有一个真正的架构角色,而抽象类几乎只是一个实现的细节,如果你正确使用它。

< P>如果java作为OOP语言来回答这个问题,java 8发布会把上述答案中的一些内容当作过时的东西。 E现在java接口可以有带有具体实现的默认方法

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

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

您希望在几个密切相关的类之间共享代码。 您希望扩展抽象类的类具有许多公共方法或字段,或者需要除public之外的访问修饰符,如protected和private。 您想要声明非静态或非最终字段。 如果出现以下情况,请考虑使用接口:

您希望不相关的类将实现您的接口。例如,许多不相关的对象可以实现可序列化接口。 您希望指定特定数据类型的行为,但不关心由谁实现其行为。 您希望利用类型的多重继承。 简单地说,我想使用

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

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


请看一看代码示例,以清楚地理解问题:

当然,理解OOP中接口和抽象类的行为以及语言如何处理它们很重要,但我认为理解每个术语的确切含义也很重要。您能想象if命令的工作方式与术语的含义不完全相同吗?而且,实际上一些语言正在减少,甚至更多,接口和抽象之间的差异。。。如果有一天碰巧这两个术语的作用几乎相同,至少你可以定义自己在哪里以及为什么要使用它们中的任何一个

如果你通读一些字典和其他字体,你可能会发现同一个词有不同的含义,但有一些共同的定义。我觉得我在书中找到的这两个意思真的非常好,非常合适

接口:

使独立的、有时不兼容的元素能够有效协调的事物或环境

摘要:

集中于更广泛或更普遍的事物或若干事物的本质的事物;本质

例如:

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

你的汽车模型是XYZ,属于ABC类型,所以它是一辆具体的汽车,一辆汽车的具体实例。汽车不是实物。事实上,创建特定对象是一组抽象的标准质量。简言之,汽车是一个抽象的类,它本身集中了任何更广泛或更普遍的事物的本质特征

应使用符合汽车手册规格的唯一燃油加注汽车油箱。实际上,没有任何限制您添加任何燃油,但发动机只有在使用指定燃油时才能正常工作,因此最好遵循其要求。这些要求表明,与其他同类汽车一样,它也接受一套标准的燃油

在面向对象的视图中,类型ABC的燃料不应声明为类,因为没有特定类型汽车的具体燃料。尽管您的汽车可以接受抽象类燃油或车用燃油,但您必须记住,您现有的车用燃油中只有一部分符合规格,即满足汽车手册要求的燃油。简言之,他们应该实现接口ABCGenreFuel,它。。。使独立的、有时不兼容的元素能够有效地协调

补遗 此外,我认为您应该记住术语类的含义,它来自前面提到的同一个站点:

类别:

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


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

这些答案太长了

接口用于定义行为

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

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

这也解释了为什么Java只支持类的单一继承,而不限制接口。因为一个具体的物体不能是不同的东西,但它可以有不同的行为;博士当您看到“是”关系时,请使用继承/抽象类。当您看到“hasaa”关系时,请创建成员变量。当您看到“依赖外部提供程序”时,实现不会继承接口。

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

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

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

ZK:反问题:如果我将其更改为纯抽象类,将所有虚拟函数标记为抽象,并且不对任何虚拟函数提供默认实现。这会使抽象类和接口相同吗?在那之后它们可以互换使用吗

回答3:接口允许多重继承,抽象类不允许

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

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

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

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

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

是一个 有一个 使用 让我们来考虑下面两个句子。HondaAccord是一辆汽车吗HondaAccord有车吗

哪一个听起来正确?通俗易懂的英语。HondaAccord和Cars有着“是一种”的关系。本田雅阁没有车。它是一辆汽车。本田雅阁有一个音乐播放器

当两个实体共享“是一个”关系时,它更适合继承。并且具有关系是创建成员变量的更好候选者。有了这一点,我们的代码如下所示:

abstract class Car
{
   string color;
   int speed;
}
class HondaAccord : Car
{
   MusicPlayer musicPlayer;
}
现在本田不生产音乐播放器。或者至少这不是他们的主要业务

因此,他们与其他公司联系并签订合同。如果你在这里接收到电源和这两根电线上的输出信号,在这些扬声器上就可以正常播放了

这使得音乐播放器成为界面的完美候选。你不在乎谁为它提供支持,只要连接工作正常

你可以用索尼或其他方式取代LG的MusicPlayer。这不会改变本田雅阁的任何一件事

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

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

为什么不能创建接口的对象?因为你不能走进陈列室说给我一个音乐播放器的合同。这没用。接口位于使用者和提供者之间,只是为了促成协议。你会如何处理协议副本?它不会播放音乐

为什么接口允许多重继承

接口不是继承的。实现了接口。接口是与外部世界交互的候选接口。本田雅阁有一个加油接口。它有为轮胎充气的接口。和给足球充气的软管一样。因此,新代码如下所示:

abstract class Car
{
    string color;
    int speed;
}
class HondaAccord : Car, IInflateAir, IRefueling
{
    MusicPlayer musicPlayer;
}

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

+1关键点在于一个类上可以实现多个接口。这是接口相对于抽象基类的一个真正优势,IMO。否则,我同意.NET设计指南,也就是说,我们更喜欢抽象基类而不是接口。尽管如此,如果您能补充一点,即它的接口也可以应用于任何类,我们会非常感兴趣。@altCognito:我想这是第二段处理的。不过,这确实提醒了我,接口是在值类型上工作的,所以我补充了这一点。非常感谢您的准确描述。这确实很有帮助。我是新来的。很遗憾你不能选择两个答案。让我困惑的一件事是您对抽象“基类”的使用。所有抽象类都意味着

子类的基类。为什么要额外命名“base”呢?虽然我认为了解两者之间的区别很重要,但在我看来,这不是一个好的面试问题。除非这份工作是写一本关于OO主题的书。你最好不要为那些讨厌的家伙工作。@Alan:我实际上喜欢这是一个面试问题,但我不会这样纠缠别人——我可能会更像是在定义层次结构时,在哪里选择接口而不是抽象基类?,或者类似的。也许他们想要的是更注重设计的答案。。。尽管像你一样,我也会把它当作一个技术问题。这里的表格差异很好:@Kave:我坚持你不能在接口中有一个公共变量。我认为接口可以有公共变量。事实上,接口中的变量是自动公开的和最终的。在我看来,接口实现了更多的Acts-as-a关系。封装比接口更好地实现组合。我不认为实现接口会属于组合。另外,接口更可能用于描述功能,比如IDisposable。它用于在类之间共享这些类能够执行某些操作的功能。更多示例IFlyable可以通过bird和plane实现。但鸟类可能起源于一类生物,而航空技术则起源于飞机。是的!状态这就是第二位面试官说界面中公共变量的奇怪方式的意思。天哪!抽象类可以有状态,接口不能!是的,其他人也都同意他们的继承方式之间的差异,这一点我忘了提,但后来已经弄明白了谢谢大家!不仅仅是陈述。。。。抽象类可以有实现。i、 例如,它们可以有一些方法,其中包含实际运行和执行某些操作的代码,这些操作由基类的实例继承和执行。。。interfacesEven并非如此,从某种意义上说,抽象类可以实例化,它们只需要使用派生类定义实例化,而不是直接实例化。但是,抽象类中定义的状态变量在通过创建派生类的实例而创建的对象中实例化。这个实例既是抽象类的实例,也是派生类的实例——它毕竟是从抽象类派生的。对于接口来说,这一切都不是真的。当您新建一个定义为实现接口的类的实例时,它不是该接口的实例,语法所做的只是让编译器检查该类的代码,并确保每个行为方法、属性、事件、eventHandler、,接口定义的等已经在类的代码中实现。谢谢。我想,既然你的回答提到了国家+对所有其他问题的良好概述,我就把你的回答标记为最终答案。你是对的,我要求的是通用OO,因为我的第一位面试官要求的是通用OO,但由于我是一个C类的人,我倾向于忘记这一点同样感谢C++解释,就像C++一样令人兴奋。我认为米迦勒解释的一个要点是,在实现一个接口时,必须实现接口中的所有成员,但是当从抽象类继承时,子类不需要实现其父类的成员+1:我敢打赌,主持访谈的猴子甚至没有意识到其他语言实现OO的方式不同。@JL我不知道问题出在哪里。您似乎混淆了抽象方法和抽象类。抽象方法没有实现。然而,在抽象类内部,有些方法可以是抽象的,例如。注意,在Java8中,您现在可以在接口中使用默认方法和静态方法,这意味着Java接口可以使用实现。参考很明显,你主要指的是.NET,所以这只是一个关于Java的观察。我非常喜欢这个类比,它用一个简单的例子来解释一个稍微复杂的主题。这是理解复杂OO术语的最好方法。简言之,只有当你能实际运用它时,所有的理论才有价值@Jay you rexample真的很容易掌握那么几个要点大多是穿透心灵而不是全神贯注!我还是有点困惑。比如说,你现在获得了F-16和T-38资格,所以现在Jay类不能继承多个C-141-pilot、F-16-pilot和T-38-pilot类,这是否意味着谁的类应该成为接口?很多人对Alex的评论给予了正确的+1,因为它揭示了本例中的一些弱点。首先,我想说,Jay将是C-141Pilot的一个实例,而不是它自己的类。此外,由于在美国空军99%的飞行员只在一架飞机上合格,FCF和试验飞行员是显著的例外,我没有考虑多个资格,以及如何可能实施。据我所知

50年前,一名飞行员同时在25架不同的飞机上获得资格,我认为这说明了我们不想使用多重继承。由于一名飞行员不太可能一次驾驶多架飞机,这将是一个实施战略模式的好机会。飞行员将拥有一系列证书,并在运行时选择正确的证书。认证将被编码为实现IFlyPlane接口的行为,包括起飞、着陆和弹出方法。我相信你的意思是DI工具可以注入实现接口的类。有些这样的工具还可以注入从抽象类派生的类,或者你是说这是不可能的?我认为更好的类比应该是使用Fuel,它将显示接口的契约性质。@Pureferret如果accelerate是汽车抽象类的核心行为的一部分,那么我不能说accelerate显示契约性质。什么是合同性质?为什么每当我们谈到界面时都会出现“契约”这个词?@overexchange,因为界面通常是两个“表面”相遇的地方,但“契约”一词意味着两个“表面”如何相遇存在一致性。至少对我来说,产生废气是你“同意”的事情是没有道理的。“但对我来说,你可以同意使用燃料也是有道理的。”Pureferret我在same@Pureferret若接口需要有外围行为,那个么为什么公共接口列表扩展集合{}被设计来描述列表的核心行为呢?这实际上与普拉桑的答案相矛盾。集合和列表都是这里的接口。我非常喜欢这个答案,因为有时很难通过观察更抽象的东西(如意图)来回答事物之间的差异,而不仅仅是结构上的差异,一个接口和一个纯抽象类几乎是一样的。列举一个抽象类和一个接口在一种特定语言中可以做什么很容易,但是创建一个抽象来赋予对象意义和责任更难,你所说的完全恢复了OO中2概念的使用。谢谢@达南杰:我知道身高是如何从动物类的概念中分离出来的,也可以从另一个不同的类中分离出来,但你所说的类间交流到底是什么意思?这仅仅是为自己的阶级定义高度,对吗?@AbdullahShoaib是-a,任何人都能做到,但不能做到,这是有区别的。这是最基本的原因,我们需要接口。can-do行为也将是抽象类的一部分。我不同意这一点:抽象类必须至少有一个抽象方法。有一个抽象类而没有抽象方法是可能的,只要您实现它。引用:抽象类是一个声明为抽象的类,它可能包含也可能不包含抽象方法。参考资料来源:你谈论的是技术细节和实现,你没有从总体上回答这个问题。这是最好的解释之一,因为有这些例子。这对我来说是有意义的,不会伤脑筋。我只是想给我的学生们举个汽车的例子。谢谢你花时间把这些放在一起。太多的绒毛,不要让它听起来比现在更让人困惑。接口没有任何属性不是真的。@Bigeyes,java不允许在接口中使用属性。我觉得其他语言也一样。你能再解释一下吗?我指的是C/.Net。请看@Bigeyes for C,其中接口可以有属性,这不会重新引入多重继承问题吗?当一个类使用多个定义了相同属性的接口时会发生什么?只是好奇thanks@happycoder:re:通过使用接口而不是抽象类,您不必担心它的属性。它有触摸屏或拨号板吗?是固定电话还是移动电话。你只需要知道它是否可拨;它的继承者是否实现了可拨号接口。-你能在代码示例中显示这一点,也没有看到它是如何继承的……一周前我问了同样的问题,我对java没有经验,但我现在已经用C++工作了一段时间了。面试官在提问之前没有指定语言,所以我只是解释说本例中的接口是抽象类,没有任何状态或实现。我同意这也是一个奇怪的问题。使用水平和垂直这两个术语可以很清楚地想象其中的区别。这很好地总结了这个问题的答案。功能实现vs结构扩展,很好!定义得更快。这有意义吗?这到底是什么意思?抽象类上函数调用的操作码比接口上函数调用的操作码快?@denis631抽象类比接口稍快,因为
接口方法中涉及搜索和调用。读这篇非常简短的文章。如果您能提供一个代码片段,这将是如此完美。