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

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

接口:

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

抽象类:

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

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

另请参阅:

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


当前回答

由于您可能已经从专家那里获得了理论知识,所以我不会花太多的时间来重复这里的所有内容,而是让我用一个简单的例子来解释,我们可以使用/不能使用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的帮助下通过自己的制动技术实现多重继承。

其他回答

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

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

我希望这篇文章有用。

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

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

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

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?

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

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

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

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

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

例如,汽车和卡车共享汽车抽象类的许多核心财产和行为,但它们也共享一些外围行为,如生成排气,即使是像司钻或发电机这样的非汽车类也共享,并不一定定义汽车或卡车,因此汽车、卡车、司钻和发电机都可以共享同一个接口IExhaust。

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