接口与抽象类(通用OO)

时间:2009-04-17 16:42:39

标签: oop interface abstract-class

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

根据我的经验,我认为以下是正确的。如果我错过了重点,请告诉我。

接口

接口中声明的每个单元都必须在子类中实现。 接口中只能存在事件,代理,属性(C#)和方法。一个类可以实现多个接口。

抽象类:

只有抽象方法必须由子类实现。 Abstract类可以有实现的常规方法。 Abstract类还可以在Events,Delegates,Properties和Methods旁边有类变量。一个类只能实现一个抽象类,因为C#中不存在多重继承。

  1. 毕竟,面试官提出了一个问题“如果你有一个只有抽象方法的抽象类怎么办?那么它与界面会有什么不同?”我不知道答案,但我认为这是上面提到的继承权吗?

  2. 另一位采访者问我,如果界面内有一个Public变量,那么它与Abstract Class有什么不同?我坚持认为你不能在界面中有一个公共变量。我不知道他想听到什么,但他也不满意。

  3. 另见

35 个答案:

答案 0 :(得分:819)

比喻怎么样:当我在空军时,我去了飞行员训练,并成为美国空军(美国空军)的飞行员。那时我没有资格飞行任何东西,不得不参加飞机类训练。一旦我获得资格,我就是一名飞行员(抽象班)和一名C-141飞行员(具体班级)。在我的一项任务中,我获得了额外的职责:安全官。现在我仍然是一名飞行员和一名C-141飞行员,但我也履行了安全官职责(我实施了ISafetyOfficer,可以这么说)。一名飞行员不需要担任安全官员,其他人也可以这样做。

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

  • Pilot:抽象类
  • C-141飞行员:具体课程
  • ISafety Officer:interface

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

答案 1 :(得分:692)

虽然你的问题表明它是针对“普通OO”的,但它似乎真正关注.NET对这些术语的使用。

在.NET中(类似于Java):

  • 接口可以没有状态或实现
  • 实现接口的类必须提供该接口的所有方法的实现
  • 抽象类可能包含状态(数据成员)和/或实现(方法)
  • 抽象类可以在不实现抽象方法的情况下继承(尽管这样的派生类本身就是抽象的)
  • 接口可能是多重继承的,抽象类可能不是(这可能是接口与abtract类分开存在的关键具体原因 - 它们允许实现多重继承,从而消除了一般MI的许多问题)。 / LI>

作为一般的OO术语,差异不一定是明确定义的。例如,有些C ++程序员可能持有类似的严格定义(接口是不能包含实现的抽象类的严格子集),而有些人可能会说具有某些默认实现的抽象类仍然是接口或非抽象接口class仍然可以定义一个接口。

实际上,有一种称为非虚拟接口(NVI)的C ++习惯用法,其中公共方法是非虚拟方法,可以“窃取”私有虚拟方法:

答案 2 :(得分:209)

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

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

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

例如。汽车和卡车共享汽车抽象类的许多核心属性和行为,但它们也共享一些外围行为,如生成排气,甚至非汽车类如钻机或PowerGenerators共享,并不一定定义汽车或卡车,所以Car,Truck,Driller和PowerGenerator都可以共享同一个界面IExhaust。

答案 3 :(得分:184)

简短:抽象类用于建模类似外观类的类层次结构(例如,Animal可以是抽象类,Human,Lion,Tiger可以是具体派生类)

AND

接口用于2个相似/不相似类之间的通信,它们不关心实现接口的类的类型(例如,Height可以是接口属性,它可以由Human,Building,树。没关系,如果你可以吃,你可以游泳,你可以死或任何东西......它只是你需要有高度的事情(在你的课堂实施))。

答案 4 :(得分:77)

还有其他一些差异 -

接口不能有任何具体的实现。抽象基类可以。这允许您在那里提供具体的实现。这可以允许抽象基类实际提供更严格的契约,而界面实际上只描述了如何使用类。 (抽象基类可以包含定义行为的非虚拟成员,从而为基类作者提供更多控制。)

可以在一个类上实现多个接口。类只能从单个抽象基类派生。这允许使用接口的多态层次结构,但不允许抽象基类。这也允许使用接口进行伪多重继承。

可以在v2 +中修改抽象基类,而不会破坏API。对接口的更改正在发生变化。

[C#/ .NET特定]接口与抽象基类不同,可以应用于值类型(结构)。结构不能从抽象基类继承。这允许行为合同/使用指南应用于值类型。

答案 5 :(得分:66)

<强>继承
考虑一辆汽车和一辆公共汽车。它们是两种不同的载体。但是,他们仍然有一些共同的属性,比如他们有转向,刹车,齿轮,发动机等 因此,使用继承概念,这可以表示为以下...

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形式及其子代,如上所述。

抽象类

抽象类是不完整的对象。为了进一步理解,让我们再次考虑车辆类比 可以驾驶车辆。对?但是不同的车辆以不同的方式驾驶......例如,驾驶自行车时不能驾驶汽车 那么如何表示车辆的驱动功能呢?更难以检查它是什么类型的车辆并用它自己的功能驱动它;添加新型车辆时,您必须反复更改Driver类 这里有抽象类和方法的作用。您可以将驱动方法定义为抽象,以告知每个继承子项必须实现此功能 所以,如果你修改车辆类......

//......Code of Vehicle Class
abstract public void drive();
//.....Code continues

自行车和汽车还必须指定如何驾驶它。否则,代码将无法编译并抛出错误 简而言之,抽象类是一个部分不完整的类,它有一些不完整的函数,继承的子函数必须指定它们自己。

<强>接口 接口完全不完整。他们没有任何财产。他们只是表明继承的孩子能够做某事...... 假设您有不同类型的手机。他们每个人都有不同的方式来做不同的功能;例如:打电话给一个人。手机制造商指定了如何做到这一点。在这里,手机可以拨打一个号码 - 也就是说,它是可拨号的。让我们将其表示为一个界面。

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的函数的编写者不必担心它的属性。例如:它是否有触摸屏或拨号盘,是固定固定电话还是手机。你只需要知道它是否可以被拨打;它是否继承(或实现)Dialable接口。

更重要的是,如果有一天你用不同的方式切换Dialable

......
public static void main(String[] args) {
    Dialable myDialable = SomeLibrary.PHONE2; // <-- changed from PHONE1 to PHONE2
    SomeOtherLibrary.doSomethingUsingADialable(myDialable);
}
.....

您可以确定代码仍然可以正常工作,因为使用dialable的函数不会(也不能)依赖于Dialable接口中指定的细节以外的细节。它们都实现了Dialable接口,这是函数唯一关心的。

开发人员通常使用接口来确保对象之间的互操作性(可互换使用),只要它们共享一个共同的功能(就像您可能更改为固定电话或移动电话一样,只需要拨打一个号码)。简而言之,接口是一个更简单的抽象类版本,没有任何属性 另请注意,您可以根据需要实现(继承)任意数量的接口,但您只能扩展(继承)单个父类。

更多信息 Abstract classes vs Interfaces

答案 6 :(得分:37)

如果您将java视为OOP语言来回答此问题,那么Java 8版本会导致上述答案中的某些内容过时。现在java接口可以有默认方法和具体实现。

Oracle website提供了interfaceabstract类之间的主要区别。

如果符合以下情况,请考虑使用抽象类

  1. 您希望在几个密切相关的类之间共享代码。
  2. 您希望扩展抽象类的类具有许多常用方法或字段,或者需要除公共之外的访问修饰符(例如protected和private)。
  3. 您想要声明非静态或非最终字段。
  4. 在以下情况下考虑使用接口

    1. 您希望不相关的类会实现您的界面。例如,许多不相关的对象可以实现Serializable接口。
    2. 您希望指定特定数据类型的行为,但不关心谁实现其行为。
    3. 您希望利用类型的多重继承。
    4. 简单来说,我想使用

      接口: 通过多个不相关的对象实现合同

      抽象类: 在多个相关对象中实现相同或不同的行为

      查看代码示例,以清晰的方式理解事物:How should I have explained the difference between an Interface and an Abstract class?

答案 7 :(得分:32)

采访者正在咆哮着一棵奇怪的树。对于像C#和Java这样的语言,有区别,但在其他语言中,如C ++则没有。 OO理论并没有区分这两者,只是语言的语法。

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

在C#或Java中,没有任何实现的抽象类只与用于从中继承的语法的接口不同,而且只能从一个接口继承。

答案 8 :(得分:31)

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

答案 9 :(得分:26)

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

  1. 什么时候应该使用Interface?

    如果您不了解实施,我们有需求规范,那么我们选择接口

  2. 什么时候应该使用Abstract Class?

    如果您知道实现但不完全(部分实现),那么我们使用Abstract类。

    <强>接口

    每个方法默认公共抽象意味着接口是100%纯抽象。

    <强>抽象

    可以有Concrete方法和Abstract方法,什么是具体方法,在Abstract类中有实现,    抽象类是一个声明为abstract的类 - 它可能包含也可能不包含抽象方法。

    <强>接口

    我们不能将接口声明为私有的受保护的

    Q值。为什么我们没有声明接口是私有的和受保护的?

    因为默认情况下接口方法是公共抽象,所以我们没有将接口声明为私有和受保护。

    界面方法
       我们也不能将接口声明为private,protected,final,static,synchronized,native .....

    我会说明理由:  为什么我们不声明同步方法,因为我们不能创建接口的对象和同步是对象的工作因此和儿子的原因,我们没有声明同步方法  瞬态概念也不适用,因为瞬态工作与同步。

    摘要

    我们很高兴地使用公共的,私人的最终静态....意味着没有限制适用于抽象。

    <强>接口

    变量在接口中声明为默认的公共静态final,所以我们也没有声明变量为private,protected。

    Volatile修饰符也不适用于接口,因为接口变量默认为public static final和final变量,一旦将值赋值给变量就不能更改值,一旦将变量声明为接口,就必须分配变量。 / p>

    而且变量是变化的,所以它是对手。最终这就是我们不在界面中使用volatile变量的原因。

    <强>抽象

    抽象变量无需声明public static final。

  3. 我希望这篇文章很有用。

答案 10 :(得分:25)

从概念上讲,通过使用任何一个或两个来保持语言特定的实现,规则,好处和实现任何编程目标,可以或者不能拥有代码/数据/属性,等等,单个或多个继承,全部放在一边

1- Abstract(或纯抽象)类旨在实现层次结构。如果您的业务对象在结构上看起来有点类似,那么只表示父子(层次结构)类关系,那么将使用继承/抽象类。如果您的业务模型没有层次结构,则不应使用继承(这里我不是在谈论编程逻辑,例如某些设计模式需要继承)。从概念上讲,抽象类是一种在OOP中实现业务模型层次结构的方法,它与Interfaces无关,实际上将Abstract类与Interface进行比较是没有意义的,因为两者在概念上完全不同,在访谈中只是要检查概念因为它看起来在实现时都提供了相同的功能,我们程序员通常更多地强调编码。 [请记住,抽象与抽象类不同]。

2- an Interface是一个契约,是由一组或多组函数表示的完整业务功能。这就是为什么它被实现而不是继承。业务对象(层次结构的一部分或非层次结构)可以具有任意数量的完整业务功能。它与抽象类无关,一般意味着继承。例如,人类可以运行,大象可以运行,鸟可以运行,等等,所有这些不同层次的对象都将实现RUN接口或EAT或SPEAK接口。不要进入实现,因为您可能将其实现为具有实现这些接口的每种类型的抽象类。任何层次结构的对象都可以具有与其层次结构无关的功能(接口)。

我相信,Interfaces并不是为了实现多重继承或公开公共行为而发明的,同样,纯粹的抽象类不是否决于接口,而是Interface是一个对象可以做的功能(通过该接口的功能)和Abstract Class表示层次结构的父级,用于生成具有父级

的核心结构(属性+功能)的子级

当被问及差异时,除非明确询问,否则实际上是概念差异而不是语言特定实现的差异。

我相信,两位采访者都期待这两者之间存在一条直线上的差异,当你失败时,他们试图通过实施ONE作为其他来驱使你走向这种差异

  

如果你的抽象类只有抽象方法怎么办?

答案 11 :(得分:21)

对于.Net,

你对第二位面试官的回答也是对第一位面试官的回答......抽象类可以有实现,AND状态,接口不能......

编辑:另一方面,我甚至不会使用短语“子类”(或“继承”短语)来描述“定义为实现”接口的类。对我来说,接口是一个合同的定义,如果一个类被定义为“实现”该接口,那么该类必须符合该合同。它不会继承任何东西......你必须自己添加一切,明确地。

答案 12 :(得分:17)

我认为他们不喜欢你的回答,因为你给出了技术差异而不是设计差异。这个问题对我来说就像一个巨魔问题。事实上,接口和抽象类具有完全不同的性质,因此您无法真正比​​较它们。我将向您展示我对接口的作用是什么以及抽象类的作用是什么。

interface:用于确保合同并在类之间建立低耦合,以便拥有更易维护,可扩展且可测试的应用程序。

抽象类:仅用于对具有相同责任性的类之间的某些代码进行分解。请注意,这是多重继承在OOP中是一件坏事的主要原因,因为class shouldn't handle many responsabilities(改为使用composition)。

所以接口具有真正的架构角色,而抽象类几乎只是实现的细节(如果你当然正确使用它)。

答案 13 :(得分:16)

接口 :如果您想在组件上暗示规则可能会或可能不会 相互关联

<强>优点:

  1. 允许多重继承
  2. 通过不暴露在上下文中使用的确切类型的对象来提供抽象
  3. 通过合同的特定签名提供一致性
  4. <强>缺点:

    1. 必须实施所有定义的合同
    2. 不能拥有变量或代理
    3. 一旦定义了,就不能在不破坏所有类的情况下进行更改
    4. 抽象类 :应该在您希望对彼此相关的组件有一些基本或默认行为或实现的地方使用

      <强>优点:

      1. 比界面更快
      2. 具有实施的灵活性(您可以完全或部分实施)
      3. 可以在不破坏派生类的情况下轻松更改
      4. <强>缺点:

        1. 无法实例化
        2. 不支持多重继承

答案 14 :(得分:13)

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?" 

Docs清楚地说,如果抽象类只包含抽象方法声明,则应将其声明为接口。

An another interviewer asked me what if you had a Public variable inside
the interface, how would that be different than in Abstract Class?

接口中的变量默认为public static和final。如果抽象类中的所有变量都是公共的,那么问题就可以被设置为框架?好吧,与接口中的变量不同,它们仍然可以是非静态的,非最终的。

最后,我将再添加一点上述内容 - 抽象类仍然是类,属于单个继承树,而接口可以存在于多重继承中。

答案 15 :(得分:12)

  1. 接口:
    • 我们没有实现(或定义)方法,我们在派生类中这样做。
    • 我们不在接口中声明成员变量。
    • 接口表达HAS-A关系。这意味着它们是物体的掩饰。
  2. 抽象类:
    • 我们可以在抽象类中声明和定义方法。
    • 我们隐藏它的构造函数。这意味着没有直接从它创建的对象。
    • 抽象类可以包含成员变量。
    • 派生类继承到抽象类,表示派生类中的对象未被屏蔽,它继承到抽象类。这种情况下的关系是IS-A。
  3. 这是我的意见。

答案 16 :(得分:12)

由Jeffrey Richter通过C#从CLR复制......

我经常听到这样一个问题,“我应该设计一个基本类型还是一个界面?”答案并不总是明确的。

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

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

■■易于使用作为开发人员,您通常更容易定义从a派生的新类型 基类型比实现接口的所有方法。基类型可以提供 很多功能,因此派生类型可能只需要对其行为进行相对较小的修改。如果提供接口,则新类型必须实现所有成员。

■■一致的实施无论接口合同的记录程度如何,都是如此 每个人都不太可能100%正确地执行合同。实际上,COM 遇到这个问题,这就是为什么有些COM对象只能正常工作 微软 Word或Windows Internet Explorer。通过提供良好的基础类型 默认实现,你开始使用一个有效的类型并经过充分测试;你可以 修改需要修改的部分。

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

答案 17 :(得分:10)

由于您可能从专家那里获得了理论知识,我在这里重复所有这些并没有花太多的话,而是让我用一个简单的例子来解释我们可以使用/不能使用Interface和{{ 1}}。

考虑您正在设计一个应用程序来列出Cars的所有功能。在各个方面,您需要共同继承,因为DigitalFuelMeter,Air Conditioning,Seat adjustment等一些属性对于所有汽车来说都很常见。同样,我们只需要某些类的继承,因为制动系统(ABS,EBD)等一些属性仅适用于某些汽车。

以下课程充当所有汽车的基类:

Abstract class

考虑我们为每辆车都有一个单独的班级。

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

考虑我们需要一种方法来继承Verna和Cruze汽车的制动技术(不适用于Alto)。虽然两者都使用制动技术,但“技术”却不同。所以我们创建了一个抽象类,其中方法将被声明为Abstract,它应该在其子类中实现。

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和科鲁兹实施制动系统的类型:

public abstract class Brake
{
    public abstract string GetBrakeTechnology();
}

看到上面两个类的问题?它们继承自C#.Net不允许的多个类,即使该方法是在子代中实现的。这需要接口。

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

实施如下:

interface IBrakeTechnology
{
    string GetBrakeTechnology();
}

现在Verna和Cruze可以借助Interface实现其自身制动技术的多重继承。

答案 18 :(得分:10)

接口定义服务或服务集的合同。它们以水平方式提供多态性,因为两个完全不相关的类可以实现相同的接口,但可以互换地用作它们实现的接口类型的参数,因为这两个类都承诺满足接口定义的服务集。接口不提供实现细节。

抽象类定义其子库的基本结构,以及可选的部分实现。抽象类以垂直但有方向的方式提供多态性,因为任何继承抽象类的类都可以被视为该抽象类的实例,而不是相反。抽象类可以并且经常包含实现细节,但不能自己实例化 - 只有它们的子类可以“新建”。

C#确实允许接口继承,请注意。

答案 19 :(得分:10)

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

界面协议。它指定:&#34;这就是我们彼此交谈的方式&#34;。它没有任何实现,因为它不是假设有任何实现。这是一份合同。它类似于C中的.h头文件。

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

基本上任何类(抽象与否)都是关于 的内容,而界面是关于你如何使用的。例如:Animal可能是一个抽象类,它实现了一些基本的代谢功能,并指定了呼吸和运动的抽象方法而没有给出实现,因为它不知道它是应该通过鳃还是肺呼吸,以及它是否会飞,游泳,散步或爬行。另一方面,Mount可能是一个接口,它指明你可以骑动物,而不知道它是什么类型的动物(或者它是否是动物!)。 p>

事实上,在幕后,界面基本上是一个只有抽象方法的抽象类,并不重要。从概念上讲,他们完全扮演着不同的角色。

答案 20 :(得分:9)

接口是强制执行特定行为的轻量级方式。这是一种思考方式。

答案 21 :(得分:6)

界面类型与抽象基类

改编自Pro C# 5.0 and the .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#不支持类的多重继承。 因此,如果您想创建一个CarVan并且是CloneableType的MiniVan,则无法执行此操作:

// Nope! Multiple inheritance is not possible in C#
// for classes.
public class MiniVan : Car, CloneableType
{
}

正如您所猜测的,接口类型可以解决。定义接口后,它可以 由任何类或结构,任何层次结构,任何命名空间或任何程序集中实现 (用任何.NET编程语言编写)。如您所见,接口具有高度多态性。 考虑在System命名空间中定义的名为ICloneable的标准.NET接口。这个 interface定义了一个名为Clone()的方法:

public interface ICloneable
{
object Clone();
}

答案 22 :(得分:6)

1)一个接口可以看作是一个纯粹的抽象类,是相同的,但尽管如此,实现一个接口并从一个抽象类继承是不一样的。当您从这个纯抽象类继承时,您正在定义一个层次结构 - &gt;继承,如果你实现了你不是的接口,你可以实现任意数量的接口,但你只能从一个类继承。

2)您可以在接口中定义属性,因此实现该接口的类必须具有该属性。

例如:

  public interface IVariable
  {
      string name {get; set;}
  }

实现该接口的类必须具有类似的属性。

答案 23 :(得分:6)

虽然这个问题已经很久了,但我想补充一点,转而支持接口:

可以使用任何依赖注入工具注入接口,而极少数支持抽象类注入。

答案 24 :(得分:6)

回答第二个问题:public中定义的interface变量默认为static final,而public类中的abstract变量是实例变量。

答案 25 :(得分:5)

来自another answer of mine,主要涉及何时使用其中一种:

  

根据我的经验,界面是最好的   当你有几个课时使用   每个人都需要回应同样的事情   方法或方法使它们可以   其他代码可互换使用   这将是针对那些   类的通用接口。最好的   使用界面的时候   协议很重要但是   底层逻辑可能不同   每节课。如果你不这样做   复制逻辑,考虑抽象   类或标准类继承   代替。

答案 26 :(得分:4)

从编码角度来看

如果抽象类只有抽象方法,那么接口可以替换抽象类。否则,将Abstract类更改为接口意味着您将失去继承提供的代码可重用性。

从设计角度来看

如果它是“是一种”关系并且您需要子集或所有功能,请将其保留为抽象类。如果它是“应该做”的关系,请将其保留为接口。

决定您需要什么:只是政策执行或代码可重用性和政策。

答案 27 :(得分:4)

For sure it is important to understand the behavior of interface and abstract class in OOP (and how languages handle them), but I think it is also important to understand what exactly each term means. Can you imagine the if command not working exactly as the meaning of the term? Also, actually some languages are reducing, even more, the differences between an interface and an abstract... if by chance one day the two terms operate almost identically, at least you can define yourself where (and why) should any of them be used for.

If you read through some dictionaries and other fonts you may find different meanings for the same term but having some common definitions. I think these two meanings I found in this site的执行速度真的非常非常适合。

界面:

  

允许单独且有时不兼容的元素有效协调的事物或环境。

摘要:

  

某种东西集中在任何更广泛或更一般或几件事物的基本品质上;本质。

示例:

你买了一辆车需要燃料。

enter image description here

您的汽车型号为XYZ,属于ABC类型,因此它是一辆混凝土汽车,是汽车的特定实例。汽车不是真正的物体。实际上,它是一组创建特定对象的抽象标准(质量)。简而言之,Car是抽象类,它是“集中于任何更广泛或更普遍的基本品质的东西”

应该使用符合汽车手册规格的唯一燃料来填充汽车油箱。实际上,没有什么可以限制你投入任何燃料,但发动机只能使用指定的燃料才能正常工作,因此最好遵循其要求。要求说,它接受同样类型ABC的其他汽车,是一套标准的燃料。

在面向对象的视图中,不应将类型ABC的燃料声明为类,因为那里没有特定类型汽车的混凝土燃料。虽然您的汽车可以接受抽象类燃料或车辆燃料,但您必须记住,您现有的唯一一种车辆燃料符合规格,那些符合您汽车手册要求的规格。简而言之,他们应该实现接口 ABCGenreFuel,其中“...使单独的,有时不兼容的元素能够有效地协调”

附录

此外,我认为你应该记住术语类的含义,它是(来自前面提到的同一站点):

类别:

  

由于共同属性,特征,品质或特征而被视为组成群体的若干人或事物;样;

这样,类(或抽象类)不应仅表示公共属性(如接口),而应表示具有公共属性的某种组。界面不需要代表一种。它必须代表共同的属性。这样,我认为类和抽象类可以用来表示不应该经常改变其方面的东西,比如人类是哺乳动物,因为它代表了某种类型。种类不应经常改变自己。

答案 28 :(得分:3)

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

一个实时示例是标记为ISO的任何产品。ISO提供了关于如何构建产品以及rules/specification minimum set of features的{​​{1}}集合有。。

这只是Must产品必须有的.ISO会签署产品subset of properties

现在看一下这段代码

only if it satisfies the its standards

此处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:

it has all that features that a watch must suppost的目的是提供多个派生类可以共享的abstract class

例如,类库可以定义一个抽象类,该抽象类用作其许多函数的参数,并要求程序员使用该库通过创建派生类来提供自己的类实现。 common definition of a base class。实现类将完成此实现。

E.g - :假设我将一个类Abstract simply means if you cannot define it completely declare it as an abstract声明为抽象但我不知道哪个食谱是 然后我会概括这个类来定义Recipe植入配方将取决于实施菜肴。

抽象类可以包含抽象方法,也可以不是抽象方法。因此,您可以注意到Interface中的差异。不一定是实现类必须具有的每个方法。您只需要覆盖抽象方法。

简单的话语如果你想要common definition of any recipe

答案 29 :(得分:3)

其他一些差异:

抽象类可以有静态方法,属性,字段等,而运算符,接口则不能。 Cast运算符允许转换为抽象类或从抽象类转换,但不允许转换为/从接口转换。

所以你可以自己使用抽象类,即使它从未实现过(通过它的静态成员),也不能以任何方式单独使用接口。

答案 30 :(得分:3)

tl; dr;当您看到“是A”关系时,请使用继承/抽象类。当您看到“具有”关系时,请创建成员变量。当您看到“依赖外部提供程序”实现(而不是继承)接口时。

面试问题:接口和抽象类之间有什么区别?以及您如何决定何时使用什么?我主要得到以下一个或所有答案:答案1:您不能创建抽象类和接口的对象。

ZK(这是我的姓名缩写):您无法创建任何一个的对象。所以这没什么区别。这是接口和抽象类之间的相似之处。反问:为什么不能创建抽象类或接口的对象?

答案2:抽象类可以具有一个函数主体作为部分/默认实现。

ZK:反问:因此,如果将其更改为纯抽象类,则将所有虚拟函数标记为抽象,并且不为任何虚拟函数提供默认实现。这样会使抽象类和接口相同吗?在那之后可以互换使用吗?

答案3:接口允许多重继承,而抽象类则不允许。

ZK:反问:您真的从接口继承吗?还是只实现一个接口,并从抽象类继承?实施和继承有什么区别?这些反问使候选人脱颖而出,使他们大为挠头,或者只是转到下一个问题。这使我认为人们需要这些面向对象编程的基本构建模块的帮助。原始问题和所有反问题的答案均​​以英语和UML找到。您必须至少了解以下内容才能更好地理解这两种构造。

共同名词:共同名词是对同一类别或同类事物“共同”使用的名称。例如水果,动物,城市,汽车等。

专有名词:专有名词是物体,地点或事物的名称。苹果,猫,纽约,本田雅阁等。

汽车是一个普通名词。本田雅阁是一个专有名词,可能是一个合成专有名词,这是一个由两个名词组成的专有名词。

来到UML部分。您应该熟悉以下关系:

  • 是A
  • 有A
  • 使用

让我们考虑以下两个句子。 -本田雅阁是汽车吗? -本田雅阁有车吗?

哪个听起来正确?普通英语和理解能力。本田雅阁和汽车共享“是”关系。本田雅阁没有汽车。这是辆车。本田雅阁“里面有一个”音乐播放器。

当两个实体共享“是”关系时,则更适合继承。并且具有关系是创建成员变量的更好的选择。建立好之后,我们的代码如下:

abstract class Car
{
   string color;
   int speed;
}
class HondaAccord : Car
{
   MusicPlayer musicPlayer;
}

现在,本田不生产音乐播放器。或至少不是他们的主要业务。

因此,他们与其他公司联系并签订了合同。如果您在此处接通电源,并且这两根线上的输出信号都可以在这些扬声器上正常播放。

这使Music Player成为界面的理想选择。只要连接正常,您就不必在乎谁提供支持。

您可以用Sony或其他方法替换LG的MusicPlayer。而且它不会改变本田雅阁的情况。

为什么不能创建抽象类的对象?

因为您不能走进陈列室说要给我一辆车。您必须提供一个专有名词。什么车?可能是本田雅阁。那是一个销售代理可以为您带来收益的时候。

为什么不能创建界面对象?因为您不能走进陈列室说给我音乐播放器的合同。这不会有帮助。消费者和提供者之间的接口只是为了促进协议。您将如何使用协议副本?它不会播放音乐。

为什么接口允许多重继承?

接口不被继承。接口已实现。该界面是与外界互动的候选者。本田雅阁有一个加油接口。它具有用于轮胎充气的接口。还有用于给足球充气的软管。因此,新代码如下所示:

abstract class Car
{
    string color;
    int speed;
}
class HondaAccord : Car, IInflateAir, IRefueling
{
    MusicPlayer musicPlayer;
}

英语将这样写:“本田雅阁是支持充气和加油的汽车”。

答案 31 :(得分:1)

除了将Abstract classInterface进行比较之外,将Abstract classConcrete class进行比较是有意义的。

在使用具体类时使用抽象类除了 不希望/无法实例化它们而不进行扩展。并使您不想要/不能实现抽象方法的方法。

如果你喜欢类比,可以将chrome视为一个抽象类(你不能将它用作浏览器,因此无法实例化),chrome和opera作为具体类派生自chrome和浏览器附加结构作为接口。 / p>

答案 32 :(得分:1)

抽象类处理有效地打包类功能,而接口用于意图/契约/通信,并且应该与其他类/模块共享。

使用抽象类作为合同和(部分)合同实施者违反了SRP。使用抽象类作为契约(依赖)会限制创建多个抽象类以获得更好的可重用性。

在下面的示例中,使用抽象类作为OrderManager的合同会产生问题,因为我们有两种不同的处理订单的方式 - 基于客户类型和类别(客户可以是直接或间接或黄金或白银)。因此,接口用于合同,抽象类用于不同的工作流程实施

public interface IOrderProcessor
{
    bool Process(string orderNumber);
}

public abstract class CustomerTypeOrderProcessor: IOrderProcessor
{
    public bool Process(string orderNumber) => IsValid(orderNumber) ? ProcessOrder(orderNumber) : false;

    protected abstract bool ProcessOrder(string orderNumber);

    protected abstract bool IsValid(string orderNumber);
}

public class DirectCustomerOrderProcessor : CustomerTypeOrderProcessor
{
    protected override bool IsValid(string orderNumber) => string.IsNullOrEmpty(orderNumber); 

    protected override bool ProcessOrder(string orderNumber) => true; 
}

public class InDirectCustomerOrderProcessor : CustomerTypeOrderProcessor
{
    protected override bool IsValid(string orderNumber) => orderNumber.StartsWith("EX");

    protected override bool ProcessOrder(string orderNumber) => true;
}

public abstract class CustomerCategoryOrderProcessor : IOrderProcessor
{
    public bool Process(string orderNumber) => ProcessOrder(GetDiscountPercentile(orderNumber), orderNumber);

    protected abstract int GetDiscountPercentile(string orderNumber);

    protected abstract bool ProcessOrder(int discount, string orderNumber);
}

public class GoldCustomer : CustomerCategoryOrderProcessor
{
    protected override int GetDiscountPercentile(string orderNumber) => 15;

    protected override bool ProcessOrder(int discount, string orderNumber) => true;

}

public class SilverCustomer : CustomerCategoryOrderProcessor
{
    protected override int GetDiscountPercentile(string orderNumber) => 10;

    protected override bool ProcessOrder(int discount, string orderNumber) => true;

}

public class OrderManager
{
    private readonly IOrderProcessor _orderProcessor;// Not CustomerTypeOrderProcessor or CustomerCategoryOrderProcessor 

    //Using abstract class here would create problem as we have two different abstract classes
    public OrderManager(IOrderProcessor orderProcessor) => _orderProcessor = orderProcessor;
}

答案 33 :(得分:0)

接口:

  

不包含实施   接口可以从多个接口继承(支持多重继承)   成员自动公开   可能包含属性,方法,事件和索引器

抽象类:

  

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

这些是抽象类和接口之间的主要区别。

答案 34 :(得分:0)

这些答案都太长了。

  • 接口用于定义行为。

  • 抽象类用于定义事物本身,包括其行为。这就是为什么我们有时会创建一个带有一些额外属性的抽象类来继承接口的原因。

这也解释了为什么Java只支持类的单一继承,而对接口没有任何限制。因为一个具体的对象不能是不同的事物,但是它可以具有不同的行为。