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

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

接口:

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

抽象类:

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

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

另请参阅:

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


当前回答

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

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

其他回答

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

由于您可能已经从专家那里获得了理论知识,所以我不会花太多的时间来重复这里的所有内容,而是让我用一个简单的例子来解释,我们可以使用/不能使用Interface和Abstract类。

假设您正在设计一个应用程序来列出汽车的所有功能。在不同的方面,您需要共同继承,因为一些财产,如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)。虽然两者都使用制动技术,但“技术”不同。因此,我们正在创建一个抽象类,其中方法将声明为abstract,并且应该在其子类中实现。

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的帮助下通过自己的制动技术实现多重继承。

接口:

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

抽象类:

可能/可能不包含实施;至少有一个成员不会被执行。类可以从单个基类继承;不允许多重继承。成员具有访问修饰符可以包含字段、财产、构造函数、析构函数、方法、事件和索引器

这些是抽象类和接口之间的关键区别。

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?

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

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

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