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

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

接口:

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

抽象类:

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

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

另请参阅:

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


当前回答

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

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

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

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

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

其他回答

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

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

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

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

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

Pilot:抽象类C-141试验:混凝土等级ISafety Officer:接口

补充说明:这是一个类比,以帮助解释概念,而不是编码建议。看到下面的各种评论,讨论很有趣。

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

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

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

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

我希望这篇文章有用。

Jeffrey Richter通过C#从CLR复制。。。

我经常听到这样一个问题,“我应该设计一个基类型还是一个接口?”答案并不总是清晰明了。

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

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

■■ 易用性作为开发人员,定义从基类型,而不是实现接口的所有方法。基本类型可以提供许多功能,因此派生类型可能只需要对其行为进行相对较小的修改。如果提供接口,则新类型必须实现所有成员。

■■ 一致的实施无论接口合同记录得多么好每个人都不可能100%正确地履行合同。事实上,COM这就是为什么某些COM对象只能与微软Word或Windows Internet Explorer。通过为基础类型提供默认实现,您首先使用一种有效且经过良好测试的类型;那么你可以修改需要修改的零件。

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