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

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

接口:

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

抽象类:

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

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

另请参阅:

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


当前回答

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

赞成的意见:

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

欺骗:

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

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

赞成的意见:

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

欺骗:

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

其他回答

对于.Net,

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

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

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

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

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

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

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

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?" 

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

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

默认情况下,接口中的变量是公共静态变量和最终变量。问题的框架可以是,如果抽象类中的所有变量都是公共的呢?与接口中的变量不同,它们仍然可以是非静态和非最终的。

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

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

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表单及其子表单,如上文所示。

抽象类

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

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

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

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

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

在这里,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);
}
.....

通过使用接口而不是抽象类,使用Dialable的函数的编写者无需担心其财产。它有触摸屏或拨号盘吗?是固定固定电话还是移动电话。你只需要知道它是否可以拨号;它是否继承(或实现)可拨号接口。

更重要的是,如果有一天你将可拨号电话换成另一个

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

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

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

更多信息抽象类与接口

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

我们什么时候应该使用界面?如果您不了解实现,我们只有需求规范,那么我们就使用Interface我们什么时候应该使用抽象类?如果您知道实现但不完全(部分实现),那么我们使用抽象类。界面默认情况下,每个方法的公共抽象意味着接口是100%纯抽象的。摘要可以有Concrete方法和Abstract方法,抽象类是一个声明为抽象的类,它可以包含也可以不包含抽象方法。界面我们不能将接口声明为私有的、受保护的问:为什么我们不将接口声明为私有和受保护的?因为默认情况下接口方法是公共抽象的,所以我们没有将接口声明为私有和受保护的。接口方法我们也不能将接口声明为private、protected、final、static、synchronized、native。。。。。我会给出原因:为什么我们不声明synchronized方法,因为我们无法创建接口的对象,而synchronization是在对象上工作的,这也是我们不声明synchronized的原因瞬态概念也不适用,因为瞬态工作与同步。摘要我们很乐意使用公共的、私人的最终静态。。。。表示抽象上不适用任何限制。界面默认情况下,变量在接口中声明为公共静态final,因此我们也不会将变量声明为私有的受保护变量。易失性修饰符也不适用于接口,因为接口变量默认为公共静态final和final变量。一旦它将值分配给变量,您就不能更改该值,一旦您将变量声明给接口,您就必须分配该变量。易失性变量是不断变化的,所以它是最后一个,这就是我们不在接口中使用易失性的原因。摘要抽象变量不需要声明公共静态final。

我希望这篇文章有用。