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

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

接口:

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

抽象类:

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

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

另请参阅:

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


当前回答

接口:-==合约。无论哪个类实现它都必须遵循接口的所有规范。

一个实时的例子是任何ISO标记的产品。ISO给出了一套规则/规范,说明产品应该如何构建以及它必须具备的最小功能集。

这只是产品必须具备的财产的子集。SO只有在产品满足其标准时才会签署产品。

现在看看这个代码

public interface IClock{       //defines a minimum set of specification which a clock should have

    public abstract Date getTime();
    public abstract int getDate();
}
public class Fasttrack: Clock {
    // Must have getTime() and getTime() as it implements IClock
    // It also can have other set of feature like 
    public void startBackgroundLight() {
        // watch with internal light in it.
    }
    .... //Fastrack can support other feature as well
    ....
    ....
}

在这里,Fastrack被称为手表,因为它具有手表必须具备的所有功能(最小功能集)。

原因和时间摘要:

来自MSDN:

抽象类的目的是提供多个派生类可以共享的基类的公共定义。

例如,类库可以定义一个抽象类,该抽象类用作其许多函数的参数,并要求使用该库的程序员通过创建派生类来提供自己的类实现。抽象只是指如果你不能定义它,就将它完全声明为抽象。实现类将完成这个实现。

例如:假设我声明一个类配方是抽象的,但我不知道该是哪个配方然后我将推广这个类来定义任何菜谱的通用定义。菜谱的植入将取决于菜品的实现。

抽象类既可以由抽象方法组成,也可以由非抽象方法组成。因此,您可以注意到接口中的差异。因此,实现类不一定必须具有每个方法。您只需要重写抽象方法。

简单来说,如果您想要紧密耦合,请使用接口o/w,以防失去耦合抽象类

其他回答

除了将抽象类与接口进行比较之外,将抽象类和具体类进行比较是有意义的。

使用抽象类就像使用具体类一样,除非您不希望/无法在没有扩展的情况下实例化它们。让你不想/不能实现抽象方法的方法。

如果你喜欢类比,就把chrome当作一个抽象类(你不能把它当作浏览器,所以它不能被实例化),把chrome和opera当作从chrome派生的具体类,把浏览器插件结构当作接口。

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

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

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

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

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

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

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

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

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

更多信息抽象类与接口

从我的另一个答案来看,主要是关于何时使用一个而不是另一个:

根据我的经验,界面是最好的有几个类时使用每个都需要对相同的方法,以便可由其他代码互换使用将针对这些类的公共接口。最好的当协议很重要,但底层逻辑可能不同于每个类别。如果你不是复制逻辑,考虑抽象类或标准类继承相反