我最近接受了两次电话采访,被问及接口类和抽象类之间的区别。我已经解释了我能想到的每一个方面,但似乎他们在等我提一些具体的事情,我不知道是什么。
根据我的经验,我认为以下是正确的。如果我遗漏了一个要点,请告诉我。
接口:
接口中声明的每个方法都必须在子类中实现。接口中只能存在事件、委托、财产(C#)和方法。一个类可以实现多个接口。
抽象类:
子类只能实现抽象方法。抽象类可以具有具有实现的普通方法。除了事件、委托、财产和方法之外,抽象类还可以有类变量。由于C#中不存在多重继承,一个类只能实现一个抽象类。
在这之后,面试官提出了一个问题:“如果你有一个只有抽象方法的抽象类呢?这和接口有什么不同?”我不知道答案,但我认为这是上面提到的继承,对吧?另一位面试官问我,“如果你在接口中有一个公共变量,那会和抽象类中有什么不同?”我坚持认为你不能在接口中使用公共变量。我不知道他想听什么,但他也不满意。
另请参阅:
何时使用接口而不是抽象类,反之亦然接口与抽象类如何决定使用抽象类和接口?接口和抽象类之间的区别是什么?
接口类型与抽象基类
改编自Pro C#5.0和.NET 4.5 Framework书籍。
接口类型似乎与抽象基类非常相似。回忆起当类被标记为抽象时,它可以定义任意数量的抽象成员来提供所有派生类型的多态接口。然而,即使类确实定义了一组抽象成员,还可以自由定义任意数量的构造函数、字段数据、非抽象成员(使用另一方面,接口只包含抽象成员定义。抽象父类建立的多态接口有一个主要限制因为只有派生类型支持抽象父级定义的成员。然而,在更大范围内软件系统,开发多个没有共同父级的类层次结构是非常常见的超出System.Object。假定抽象基类中的抽象成员仅适用于派生类型,我们无法在不同的层次结构中配置类型以支持相同的多态性界面例如,假设您定义了以下抽象类:
public abstract class CloneableType
{
// Only derived types can support this
// "polymorphic interface." Classes in other
// hierarchies have no access to this abstract
// member.
public abstract object Clone();
}
给定此定义,只有扩展CloneableType的成员才能支持Clone()方法如果创建一组不扩展此基类的新类,则无法获得多态界面。此外,您可能还记得C#不支持类的多重继承。因此,如果您想创建一辆小型货车,它是一辆汽车,并且是一种可克隆类型,那么您无法做到:
// Nope! Multiple inheritance is not possible in C#
// for classes.
public class MiniVan : Car, CloneableType
{
}
正如您所猜测的,接口类型起到了拯救作用。定义接口后,可以可以由任何类或结构、任何层次结构、任何命名空间或任何程序集中实现(用任何.NET编程语言编写)。正如您所看到的,接口是高度多态的。考虑System命名空间中定义的名为ICloneable的标准.NET接口。这接口定义了一个名为Clone()的方法:
public interface ICloneable
{
object Clone();
}
当然,理解OOP中接口和抽象类的行为(以及语言如何处理它们)很重要,但我认为理解每个术语的确切含义也很重要。你能想象if命令不能完全按照这个词的意思工作吗?此外,实际上,一些语言正在减少,甚至更多地减少界面和抽象之间的差异。。。如果碰巧有一天这两个术语几乎相同,那么至少你可以定义它们中的任何一个应该用于何处(以及为什么)。
如果你阅读一些字典和其他字体,你可能会发现同一个词有不同的含义,但有一些共同的定义。我认为我在这个网站上找到的这两个意思真的非常好,非常适合。
接口:
使分离的、有时不相容的元素能够有效协调的事物或环境。
摘要:
集中于自身的东西,集中于任何更广泛或更普遍的事物或若干事物的本质;本质
例子:
你买了一辆车,它需要燃料。
你的汽车模型是XYZ,属于ABC类型,所以它是一辆具体的汽车,一辆汽车的具体实例。汽车不是实物。事实上,它是一组抽象的标准(质量)来创建特定的对象。简而言之,Car是一个抽象的类,它是“集中于自身的任何更广泛或更一般的东西的本质”。
应使用符合汽车手册规格的唯一燃油来加注汽车油箱。实际上,没有什么可以限制您添加任何燃油,但发动机只能在指定的燃油下正常工作,因此最好遵循其要求。该要求表示,与其他同类ABC汽车一样,该公司接受一套标准燃油。
在面向对象的视图中,ABC类型的燃料不应该被声明为一个类,因为没有特定类型的汽车的具体燃料。尽管您的汽车可以接受抽象类别燃油或车辆燃油,但您必须记住,您现有的车辆燃油中只有一部分符合规范,即满足汽车手册要求的燃油。简而言之,他们应该实现ABCGenreFuel接口,该接口“……使独立的、有时不兼容的元素能够有效地协调”。
补遗
此外,我认为你应该记住class这个词的含义,它是(来自前面提到的同一个网站):
类别:
一群人或事物由于共同的属性、特征、品质或特点而被视为组成一个群体;友善的
这样,类(或抽象类)不应只表示公共属性(如接口),而是表示具有公共属性的某种组。接口不需要表示一种。它必须表示公共属性。通过这种方式,我认为类和抽象类可以用来表示不应该经常改变其方面的事物,比如人类是哺乳动物,因为它代表了某些种类。种类不应该经常改变自己。
打个比方吧:当我在空军时,我参加了飞行员培训,成为了一名美国空军飞行员。当时我没有资格驾驶任何飞机,必须参加飞机类型的训练。一旦我获得资格,我就是一名飞行员(抽象类)和一名C-141飞行员(具体类)。在我的一项任务中,我被赋予了一项额外的职责:安全员。现在我仍然是一名飞行员和C-141飞行员,但我也履行了安全员的职责(可以说,我执行了ISafetyOfficer)。飞行员不需要是安全员,其他人也可以这样做。
所有美国空军飞行员都必须遵守某些空军规定,所有C-141(或F-16或T-38)飞行员都是“美国空军飞行员”。任何人都可以成为安全员。因此,总结如下:
Pilot:抽象类C-141试验:混凝土等级ISafety Officer:接口
补充说明:这是一个类比,以帮助解释概念,而不是编码建议。看到下面的各种评论,讨论很有趣。
接口:-==合约。无论哪个类实现它都必须遵循接口的所有规范。
一个实时的例子是任何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,以防失去耦合抽象类