ChatGPT解决这个技术问题 Extra ChatGPT

接口和抽象类有什么区别?

接口和抽象类之间到底有什么区别?

这是一个非常常见的面试问题。令人惊讶的是,与其他事物相比,抽象类很少用于解决方案。你的问题帮助了我萨夫拉兹。
这个问题也可能有助于理解接口的概念stackoverflow.com/q/8531292/1055241
我已经从这个问题中删除了 PHP 标记,因为几乎没有一个答案是特定于语言的,而且问题本身也不是特定于语言的。
回到过去,在 C++ 中,接口是一个纯抽象基类,所有方法实现 = 0。如果单个方法不 = 0,那么它有一个实现,抽象基不再是纯的,不再是接口.我认为当多重继承仅使用纯抽象基时,VMT 的间接性较少,但我不记得它们的外观了,太长了。
在 kotlin 中:接口不能存储状态 只有抽象类可以存储状态

A
Anjana Silva

接口

接口是一种契约:编写接口的人说,“嘿,我接受看起来那样的东西”,而使用接口的人说:“好吧,我写的类看起来是那样的”。

接口是一个空壳。只有方法的签名,这意味着方法没有主体。界面什么都做不了。这只是一个模式。

例如(伪代码):

// I say all motor vehicles should look like this:
interface MotorVehicle
{
    void run();

    int getFuel();
}

// My team mate complies and writes vehicle looking that way
class Car implements MotorVehicle
{

    int fuel;

    void run()
    {
        print("Wrroooooooom");
    }


    int getFuel()
    {
        return this.fuel;
    }
}

实现一个接口消耗的 CPU 很少,因为它不是一个类,只是一堆名称,因此不需要进行任何昂贵的查找。它在重要时很棒,例如在嵌入式设备中。

抽象类

与接口不同,抽象类是类。它们的使用成本更高,因为从它们继承时需要进行查找。

抽象类看起来很像接口,但它们有更多的东西:你可以为它们定义一个行为。更多的是关于一个人说,“这些类应该是这样的,它们有共同点,所以填空!”。

例如:

// I say all motor vehicles should look like this:
abstract class MotorVehicle
{

    int fuel;

    // They ALL have fuel, so lets implement this for everybody.
    int getFuel()
    {
         return this.fuel;
    }

    // That can be very different, force them to provide their
    // own implementation.
    abstract void run();
}

// My teammate complies and writes vehicle looking that way
class Car extends MotorVehicle
{
    void run()
    {
        print("Wrroooooooom");
    }
}

执行

虽然抽象类和接口应该是不同的概念,但实现有时会使这种说法不真实。有时,它们甚至不是您认为的那样。

在 Java 中,这个规则被强制执行,而在 PHP 中,接口是没有声明方法的抽象类。

在 Python 中,抽象类更像是一种可以从 ABC 模块中获得的编程技巧,并且实际上使用的是元类,因此也使用了类。接口与这种语言中的鸭子类型更相关,它是约定和调用描述符的特殊方法(__method__ 方法)的混合。

与编程一样,有另一种语言的理论、实践和实践:-)


关于接口的关键点与其说是说明类做什么,不如说是允许可以 Wizzle 的对象使自己对需要 Wizzler 的代码有用。请注意,在许多情况下,编写可以 Wizzle 的东西的人或需要 Wizzler 的人都不会是编写界面的人。
我不认为 CPU 消耗是接口上值得强调的一点。
@e-satis 你能解释一下你对 CPU 利用率的看法吗?为什么抽象类作为一个类会增加 CPU 利用率?您在这里指的是哪种查找?
@e-satis 使用Java 8,您可以在接口中定义默认方法,这相当于在抽象类中具有非抽象方法。加上这个,除了我应该使用接口之外,我再也看不到抽象类和接口之间的真正区别了,因为类可以实现多个接口但只能继承一个类
我认为 Head First Java 中的 interfaceclass 之间的比较很生动,A class defines who you are, and an interface tells what roles you could play
P
Peter Mortensen

abstract classinterface 之间的主要技术差异是:

抽象类可以有常量、成员、方法存根(没有主体的方法)和定义的方法,而接口只能有常量和方法存根。

抽象类的方法和成员可以定义为任何可见性,而接口的所有方法都必须定义为公共的(默认情况下它们被定义为公共的)。

继承抽象类时,具体的子类必须定义抽象方法,而抽象类可以扩展另一个抽象类,而不必定义父类的抽象方法。

同样,扩展另一个接口的接口不负责实现父接口的方法。这是因为接口不能定义任何实现。

子类只能扩展单个类(抽象或具体),而接口可以扩展或类可以实现多个其他接口。

子类可以定义具有相同或较少限制可见性的抽象方法,而实现接口的类必须定义具有完全相同可见性(公共)的方法。


我认为这是最好的答案,因为它突出了所有关键差异。一个例子并不是真的必要。
通常使用类,您可以从中实例化一个对象,这与 CANNOT 被实例化的抽象类不同。
@Jiazzyuser 如果抽象类实现了接口,则不必实际定义接口的方法。该要求可以推迟到继承/子具体类。但是,具体类必须实现其父类未实现的所有接口方法。我将添加示例来说明这一点。
“继承抽象类时,子类必须定义抽象方法,而接口可以扩展另一个接口,方法不必定义。” - 这不是真的。正如接口可以在不定义方法的情况下扩展接口一样,抽象类可以在不定义方法的情况下继承抽象类。
是的。 “可以用任何可见性定义抽象类的方法和成员,”
V
Vivek

一个接口只包含功能的定义/签名,如果我们有一些共同的功能以及共同的签名,那么我们需要使用一个抽象类。通过使用抽象类,我们可以同时提供行为和功能。另一个继承抽象类的开发人员可以轻松使用此功能,因为他们只需要填写空白即可。

https://i.stack.imgur.com/Xf8Yz.png

http://www.dotnetbull.com/2011/11/difference-between-abstract-class-and.html

http://www.dotnetbull.com/2011/11/what-is-abstract-class-in-c-net.html http://www.dotnetbull.com/2011/11/what-is-interface-in-c-net.html


您需要说明这适用于哪种语言(“抽象类不支持多重继承”远非普遍正确)
根据表格,最后一个比较令人困惑!接口中的方法不能是静态的,但变量是静态的 final 抽象类中实现的方法可以是静态的
接口的成员必须是 static final 。最后的说法是错误的。
我认为这个答案中的“功能”意味着“实施”。不确定“行为”是什么意思——也许是“签名”?
这里的目标编程语言是什么? C#?
K
Konamiman

可以在此处找到解释:http://www.developer.com/lang/php/article.php/3604111/PHP-5-OOP-Interfaces-Abstract-Classes-and-the-Adapter-Pattern.htm

抽象类是程序员仅部分实现的类。它可能包含一个或多个抽象方法。抽象方法只是一个函数定义,用于告诉程序员该方法必须在子类中实现。接口类似于抽象类;实际上,接口与类和抽象类占用相同的命名空间。因此,您不能定义与类同名的接口。接口是一个完全抽象的类;它的任何方法都没有实现,而不是从它继承的类,据说它实现了该接口。

无论如何,我发现这种对接口的解释有些混乱。一个更常见的定义是:接口定义了实现类必须履行的契约。接口定义由公共成员的签名组成,没有任何实现代码。


这是最正确的答案,因为 PHP 接口与其他语言的不同之处在于 PHP 接口是底层的抽象类,而其他语言的接口是类必须匹配的签名。只要没有错误,它们的行为就相同。
没错,对于 PHP,它是真正的最佳答案。但是从文本块中获取比从简单的片段中获取更难。
从您提供的定义来看,除了一个细节外,它们看起来是一样的:接口是 100% 抽象的,而抽象类是部分抽象的,可以有一些方法实现(也许所有方法都可以有实现?)。
C
Community

我不想强调许多答案中已经说过的差异(关于接口中变量的公共静态最终修饰符以及对抽象类中受保护的私有方法的支持)

简单来说,我想说:

接口:通过多个不相关的对象来实现一个契约

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

来自 Oracle documentation

在以下情况下考虑使用抽象类:

您希望在几个密切相关的类之间共享代码。您希望扩展抽象类的类具有许多公共方法或字段,或者需要公共以外的访问修饰符(例如受保护和私有)。您要声明非静态或非最终字段。

在以下情况下考虑使用接口:

您希望不相关的类会实现您的接口。例如,许多不相关的对象可以实现 Serializable 接口。您想指定特定数据类型的行为,但不关心谁实现了它的行为。您想利用类型的多重继承。

抽象类与具体类建立“是”关系。接口为类提供“具有”能力。

如果您正在寻找 Java 作为编程语言,这里还有一些更新:

Java 8 通过提供 default 方法功能在一定程度上缩小了 interfaceabstract 类之间的差距。 接口没有方法的实现现在不再有效。

有关详细信息,请参阅此文档 page

查看此 SE question 以获取代码示例以更好地理解。

How should I have explained the difference between an Interface and an Abstract class?


B
Brad Larson

一些重要的区别:

以表格的形式:

https://i.stack.imgur.com/pghvG.jpg

作为stated by Joe from javapapers

1.主要区别是Java接口的方法是隐式抽象的,不能有实现。 Java 抽象类可以具有实现默认行为的实例方法。 2.Java接口中声明的变量默认是final的。抽象类可能包含非最终变量。 3.Java接口的成员默认是公共的。 Java 抽象类可以具有类成员的常见风格,例如私有、受保护等。 4.Java 接口应该使用关键字“implements”来实现; Java 抽象类应该使用关键字“extends”进行扩展。 5.一个接口只能扩展另一个Java接口,一个抽象类可以扩展另一个Java类并实现多个Java接口。 6.一个Java类可以实现多个接口,但只能扩展一个抽象类。 7.接口是绝对抽象的,不能实例化; Java 抽象类也不能被实例化,但可以在 main() 存在的情况下被调用。 8.与java抽象类相比,java接口慢,需要额外的间接性。


我已编辑您的答案以提供正确的归属。您不能只在答案底部放一个链接。您还需要引用从其他来源复制的所有语言。此外,如果该表是从某个地方绘制的,您应该清楚地指出它来自哪里。
也请提及 C++.. 虽然 C++ 中没有关键字“接口”,但它也是一个常见的 Qn regd C++。
@cbinder:C++ 中没有关键字“接口”。 c++的区别请参考1.tutorialspoint.com/cplusplus/cpp_interfaces.htm 2.tutorialspoint.com/cplusplus/cpp_interfaces.htm
@MageshBabu 也许在包含纯虚函数的类中定义一个函数使其成为抽象类而不是接口
使用 Java 8,现在差异变小了。在此处检查更新的差异:journaldev.com/1607/…
r
ragingasiancoder

主要的一点是:

摘要是面向对象的。它提供了“对象”应该具有的基本数据和/或它应该能够执行的功能。它关注对象的基本特征:它有什么以及它可以做什么。因此,从同一抽象类继承的对象共享基本特征(泛化)。

界面是面向功能的。它定义了对象应具有的功能。不管它是什么对象,只要它能够完成接口中定义的这些功能,就可以了。它忽略了其他一切。一个对象/类可以包含几个(组)功能;因此一个类可以实现多个接口。


谢谢你,现在我们得到了一个很好的高水平响应。有趣的是,您必须深入到评论中才能找到更基于理解的回复。
其他答案太技术性了。这正朝着我认为是“正确”答案的方向发展。 OOP 的重点是语义,是否通过 CPU 昂贵的查找调用私有嵌套类公共 getter 在这里几乎无关紧要
P
Peter Mortensen

当您想在继承层次结构中提供多态行为时,请使用抽象类。

当您想要完全不相关的类的多态行为时,请使用接口。


C
Chamod Pathirana

我正在建造一座 300 层的建筑

建筑物的蓝图界面

例如,Servlet(I)

高达 200 层的建筑——部分完工——摘要

部分实现,例如通用和 HTTP servlet

建筑施工完成-混凝土

完整实现,例如自己的servlet

界面

我们对实现一无所知,只知道需求。我们可以去一个接口。

默认情况下,每个方法都是公共的和抽象的

它是一个 100% 纯抽象类

如果我们声明公开,我们就不能声明私有和受保护

如果我们声明 abstract 我们不能声明 final、static、synchronized、strictfp 和 native

每个接口都有 public、static 和 final

序列化和瞬态不适用,因为我们无法在接口中创建实例

非易失性,因为它是最终的

每个变量都是静态的

当我们在接口中声明一个变量时,我们需要在声明时初始化变量

不允许实例和静态块

抽象的

部分实施

它有一个抽象方法。另外,它使用混凝土

对抽象类方法修饰符没有限制

对抽象类变量修饰符没有限制

我们不能声明除抽象之外的其他修饰符

初始化变量没有限制

取自 DurgaJobs 网站


抽象类可以有构造函数
我完全不同意这种观点。蓝图是与“界面”完全不同的概念。蓝图更类似于特定实现的静态模型或设计规范。它更接近“类”,因为蓝图可以通过其构造函数多次实例化,但即使这样也不够接近,因为“类”还包含如何构造(ctor)的规范,以及执行的方法所以。接口作为一个概念旨在表示一些行为,例如加热/冷却,可以应用于一系列事物,例如:建筑物、烤箱等
P
Peter Mortensen

让我们再次处理这个问题:

首先要让大家知道的是,1/1和1*1的结果是一样的,但是不代表乘除法是一样的。显然,他们保持着良好的关系,但请注意你们俩是不同的。

我将指出主要区别,其余的已经解释过:

抽象类对于建模类层次结构很有用。乍一看任何需求,我们都部分清楚要构建什么,但我们知道要构建什么。所以你的抽象类是你的基类。

接口对于让其他层次结构或类知道我能做什么很有用。当你说我有能力时,你必须有那个能力。接口会将其标记为类必须实现相同的功能。


很好的答案,但数学隐喻是无用的,让我浪费了大约相当于写这篇评论的时间。现在将其乘以所有其他已阅读此问题的人。
“数学隐喻没用”,你为什么这么认为?
g
g00dnatur3

其实很简单。

您可以将接口视为只允许具有抽象方法而没有其他方法的类。

因此,接口只能“声明”而不定义您希望类具有的行为。

抽象类允许您声明(使用抽象方法)以及定义(使用完整的方法实现)您希望该类具有的行为。

常规类只允许您定义而不是声明您希望该类具有的行为/动作。

最后一件事,

在Java中,你可以实现多个接口,但你只能扩展一个(抽象类或类)......

这意味着定义的行为的继承被限制为每个类只允许一个......即,如果您想要一个封装了 A、B 和 C 类行为的类,您需要执行以下操作:A 类扩展 B,C 类扩展 A ..它有点绕道来进行多重继承......

另一方面,您可以简单地执行接口:接口 C 实现 A、B

所以实际上Java只在“声明的行为”即接口中支持多重继承,并且只支持具有定义行为的单一继承......除非你按照我描述的方式进行循环......

希望这是有道理的。


y
yegor256

接口与抽象类的比较是错误的。应该有另外两个比较:1)接口与类和2)抽象与最终类。

接口与类

接口是两个对象之间的契约。例如,我是邮递员,而你是要投递的包裹。我希望您知道您的收货地址。当有人给我一个包裹时,它必须知道它的送货地址:

interface Package {
  String address();
}

类是一组遵守约定的对象。例如,我是“盒子”组的盒子,我遵守邮递员要求的合同。同时我遵守其他合同:

class Box implements Package, Property {
  @Override
  String address() {
    return "5th Street, New York, NY";
  }
  @Override
  Human owner() {
    // this method is part of another contract
  }
}

摘要与最终

抽象类是一组不完整的对象。它们无法使用,因为它们遗漏了某些部分。例如,我是一个抽象的 GPS 感知框 - 我知道如何检查我在地图上的位置:

abstract class GpsBox implements Package {
  @Override
  public abstract String address();
  protected Coordinates whereAmI() {
    // connect to GPS and return my current position
  }
}

这个类,如果被另一个类继承/扩展,会非常有用。但就其本身而言 - 它是无用的,因为它不能有对象。抽象类可以构建最终类的元素。

final类是一组完整的对象,可以使用,但不能修改。他们确切地知道如何工作和做什么。例如,我是一个盒子,它总是在构造过程中到达指定的地址:

final class DirectBox implements Package {
  private final String to;
  public DirectBox(String addr) {
    this.to = addr;
  }
  @Override
  public String address() {
    return this.to;
  }
}

在大多数语言中,如 Java 或 C++,可能只有一个类,既不是抽象的也不是最终的。这样的类可以被继承,也可以被实例化。不过,我认为这并不完全符合面向对象的范式。

同样,将接口与抽象类进行比较是不正确的。


l
lordvidex

如果您有一些可供多个类使用的通用方法,请选择抽象类。否则,如果您希望类遵循某些明确的蓝图,请选择接口。

下面的例子证明了这一点。

Java中的抽象类:

abstract class Animals
{
    // They all love to eat. So let's implement them for everybody
    void eat()
    {
        System.out.println("Eating...");
    }
    // The make different sounds. They will provide their own implementation.
    abstract void sound();
}
 
class Dog extends Animals
{
    void sound()
    {
        System.out.println("Woof Woof");
    }
}
 
class Cat extends Animals
{
    void sound()
    {
        System.out.println("Meoww");
    }
}

以下是Java中接口的实现:

interface Shape
{
    void display();
    double area();
}
 
class Rectangle implements Shape 
{
    int length, width;
    Rectangle(int length, int width)
    {
        this.length = length;
        this.width = width;
    }
    @Override
    public void display() 
    {
        System.out.println("****\n* *\n* *\n****"); 
    }
    @Override
    public double area() 
    {
        return (double)(length*width);
    }
} 
 
class Circle implements Shape 
{
    double pi = 3.14;
    int radius;
    Circle(int radius)
    {
        this.radius = radius;
    }
    @Override
    public void display() 
    {
        System.out.println("O"); // :P
    }
    @Override
    public double area() 
    { 
        return (double)((pi*radius*radius)/2);
    }
}

简而言之,一些重要的关键点:

Java接口中声明的变量默认是final的。抽象类可以有非最终变量。 Java 接口中声明的变量默认是静态的。抽象类可以有非静态变量。默认情况下,Java 接口的成员是公共的。 Java 抽象类可以具有通常的类成员风格,如私有、受保护等。


V
Vivek Vermani

唯一的区别是一个可以参与多重继承而另一个不能。

接口的定义随着时间而改变。您是否认为接口只有方法声明并且只是合同? Java 8 之后的静态最终变量和默认定义又如何呢?

接口被引入 Java 是因为 the diamond problem 具有多重继承,而这正是他们真正打算做的事情。

接口是为解决多重继承问题而创建的结构,可以具有抽象方法、默认定义和静态最终变量。

请参阅 Why does Java allow static final variables in interfaces when they are only intended to be contracts?


虽然这是一个重要的区别,但这不是唯一的区别。
S
Sentinel

界面:转(左转,右转。)

抽象类:轮子。

类:Steering Wheel,派生自Wheel,暴露Interface Turn

一个用于对可以跨多种事物提供的行为进行分类,另一个用于对事物的本体建模。


P
Pritam Banerjee

简而言之,区别如下:

接口和抽象类之间的语法差异:

抽象类的方法和成员可以具有任何可见性。接口的所有方法都必须是公共的。 //不再适用于 Java 9 抽象类的具体子类必须定义所有抽象方法。 Abstract 子类可以有抽象方法。扩展另一个接口的接口不需要为从父接口继承的方法提供默认实现。一个子类只能扩展一个类。一个接口可以扩展多个接口。一个类可以实现多个接口。子类可以定义具有相同或更少限制可见性的抽象方法,而实现接口的类必须将所有接口方法定义为公共的。抽象类可以有构造函数,但不能有接口。 Java 9 的接口具有私有静态方法。

现在在接口中:

public static - 支持
public abstract - 支持
public default - 支持
private static - 支持
private abstract - 编译错误
private default - 编译错误
private - 支持


S
Sergiu Dumitriu

许多初级开发人员错误地将接口、抽象类和具体类视为同一事物的细微变化,并纯粹出于技术原因选择其中之一:我需要多重继承吗?我需要一些地方来放置常用方法吗?除了具体的课程之外,我还需要为其他事情烦恼吗?这是错误的,隐藏在这些问题中的是主要问题:“我”。当您自己编写代码时,您很少会想到其他现在或未来的开发人员正在处理或使用您的代码。

接口和抽象类,虽然从技术角度来看很相似,但它们的含义和目的却完全不同。

概括

接口定义了一些实现将为您完成的合同。抽象类提供您的实现可以重用的默认行为。

替代摘要

接口用于定义公共 API 抽象类用于内部使用和定义 SPI

关于隐藏实现细节的重要性

一个具体的类以一种非常具体的方式完成实际的工作。例如,ArrayList 使用连续的内存区域以紧凑的方式存储对象列表,它提供快速的随机访问、迭代和就地更改,但在插入、删除甚至偶尔添加时都很糟糕;同时,LinkedList 使用双链接节点来存储对象列表,它提供了快速迭代、就地更改和插入/删除/添加,但在随机访问时很糟糕。这两种类型的列表针对不同的用例进行了优化,如何使用它们非常重要。当您试图从您经常与之交互的列表中挤出性能时,并且在选择列表类型取决于您时,您应该仔细选择您要实例化的列表。

另一方面,列表的高级用户并不真正关心它是如何实际实现的,他们应该远离这些细节。让我们想象一下,Java 没有公开 List 接口,而只有一个具体的 List 类,这实际上就是现在的 LinkedList。所有 Java 开发人员都会定制他们的代码以适应实现细节:避免随机访问,添加缓存以加快访问速度,或者只是自己重新实现 ArrayList,尽管它与实际使用的所有其他代码不兼容仅限 List。那将是可怕的......但现在想象一下,Java 大师们实际上意识到链表对于大多数实际用例来说是可怕的,并决定为他们唯一可用的 List 类切换到数组列表。这将影响世界上每个 Java 程序的性能,人们不会对此感到高兴。罪魁祸首是实现细节是可用的,开发人员认为这些细节是他们可以依赖的永久合同。这就是为什么隐藏实现细节并只定义抽象合约很重要的原因。这就是接口的目的:定义一个方法接受什么样的输入,以及期望什么样的输出,而不会暴露所有诱使程序员调整代码以适应可能随着任何未来更新而改变的内部细节的胆量.

抽象类位于接口和具体类之间。它应该帮助实现共享通用或无聊的代码。例如,AbstractCollection 根据大小为 0、contains 为迭代和比较、addAll 为重复 add 等为 isEmpty 提供基本实现。这让实现可以专注于区分它们的关键部分:如何实际存储和检索数据。

API 与 SPI

接口是代码不同部分之间的低内聚网关。当内部发生某些变化时,它们允许库的存在和发展而不会破坏每个库用户。它被称为应用程序编程接口,而不是应用程序编程类。在较小的规模上,它们还允许多个开发人员通过有据可查的接口分离不同的模块,成功地在大型项目上协作。

抽象类是在实现接口时使用的高内聚帮助器,假设有一定程度的实现细节。或者,抽象类用于定义 SPI、服务提供者接口。

API 和 SPI 之间的区别很微妙,但很重要:对于 API,重点在于谁使用它,而对于 SPI,重点在于谁实现它。

向 API 添加方法很容易,API 的所有现有用户仍然可以编译。向 SPI 添加方法很困难,因为每个服务提供者(具体实现)都必须实现新方法。如果使用接口来定义 SPI,则只要 SPI 合同发生变化,提供者就必须发布新版本。如果改为使用抽象类,则可以根据现有抽象方法定义新方法,也可以将其定义为空的 throw not implemented exception 存根,这至少允许旧版本的服务实现仍然可以编译和运行。

关于 Java 8 和默认方法的说明

尽管 Java 8 引入了接口的默认方法,这使得接口和抽象类之间的界限更加模糊,但这并不是为了让实现可以重用代码,而是为了更容易更改既作为 API 又作为 SPI 的接口(或错误地用于定义 SPI 而不是抽象类)。

使用哪一个?

该东西是否应该被代码的其他部分或其他外部代码公开使用?向它添加一个接口以隐藏公共抽象合约的实现细节,这是事物的一般行为。这个东西应该有多个实现和很多共同的代码吗?做一个接口和一个抽象的、不完整的实现。是否永远只有一种实现,而没有其他人会使用它?让它成为一个具体的类。 “ever”是很长的时间,你可以安全地玩它,仍然在它上面添加一个界面。

推论:反之亦然:当使用一个事物时,总是尝试使用你真正需要的最通用的类/接口。换句话说,不要将您的变量声明为 ArrayList theList = new ArrayList(),除非您实际上非常强烈地依赖它是一个 array 列表,并且没有其他类型的列表会为您削减它。改用 List theList = new ArrayList,如果它是一个列表而不是任何其他类型的集合这一事实实际上并不重要,则使用 Collection theCollection = new ArrayList


C
Community

并不是原始问题的真正答案,但一旦你找到了它们之间差异的答案,你就会进入何时使用的困境:When to use interfaces or abstract classes? When to use both?

我对 OOP 的了解有限,但将接口视为语法中形容词的等价物对我来说一直有效(如果这种方法是虚假的,请纠正我!)。例如,接口名称就像您可以赋予一个类的属性或功能,而一个类可以有许多:ISerializable、ICountable、IList、ICacheable、IHappy,...


R
Rahul Chauhan

您可以发现接口和抽象类之间的明显区别。

界面

接口只包含抽象方法。

实现接口时强制用户实现所有方法。

仅包含最终变量和静态变量。

使用 interface 关键字声明。

接口的所有方法都必须定义为公共的。

一个接口可以扩展,或者一个类可以实现多个其他接口。

抽象类

抽象类包含抽象和非抽象方法。

继承抽象类时不强制用户实现所有方法。

包含各种变量,包括原始变量和非原始变量

使用抽象关键字声明。

抽象类的方法和成员可以用任何可见性来定义。

子类只能扩展单个类(抽象或具体)。


Z
Ziaullah Khan

我迟到了 10 年,但我想尝试任何方式。几天前写了一篇关于相同的帖子。想把它贴在这里。

tl;博士;当您看到“Is A”关系时,请使用继承/抽象类。当你看到“有”关系时,创建成员变量。当您看到“依赖于外部提供者”时,实现(而不是继承)一个接口。

面试题:接口和抽象类有什么区别?你如何决定何时使用什么?我大多得到以下一个或全部答案: 答案 1:您不能创建抽象类和接口的对象。

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

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

ZK:反问:所以如果我把它改成一个纯抽象类,把所有的虚函数都标记为抽象,并且不为任何虚函数提供默认实现。那会使抽象类和接口相同吗?之后它们可以互换使用吗?

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

ZK:反问:你真的继承自一个接口吗?还是您只是实现一个接口并从抽象类继承?实现和继承有什么区别?这些反题让候选人望而却步,让大多数人摸不着头脑,或者直接跳到下一个问题。这让我觉得人们在面向对象编程的这些基本构建块方面需要帮助。原始问题和所有反问题的答案都可以在英语和 UML 中找到。您必须至少了解以下内容才能更好地理解这两个结构。

普通名词:普通名词是给同一类或同类事物“共同”的名称。例如水果、动物、城市、汽车等。

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

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

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

是一个

有个

用途

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

哪一个听起来正确?简单的英语和理解力。 HondaAccord 和 Cars 共享“Is A”关系。本田雅阁没有汽车。这是辆车。本田雅阁“有一个”音乐播放器。

当两个实体共享“Is A”关系时,它是更好的继承候选者。并且有一个关系是创建成员变量的更好候选者。有了这个,我们的代码看起来像这样:

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

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

所以他们联系其他公司并签订合同。如果您在此处接收电源并在这两条线上接收输出信号,它将在这些扬声器上正常播放。

这使得音乐播放器成为界面的完美候选者。只要连接工作正常,您不在乎谁为其提供支持。

您可以用索尼或其他方式替换 LG 的 MusicPlayer。它不会改变本田雅阁的任何事情。

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

因为你不能走进陈列室说给我一辆车。你必须提供一个专有名词。什么车?可能是本田雅阁。那时销售代理可以为您提供一些东西。

为什么不能创建接口的对象?因为你不能走进陈列室说给我一份音乐播放器的合同。它不会有帮助。接口位于消费者和提供者之间,只是为了促进达成协议。您将如何处理该协议的副本?它不会播放音乐。

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

接口不被继承。实现了接口。接口是与外部世界交互的候选者。本田雅阁有一个加油接口。它有给轮胎充气的接口。还有用来给足球充气的软管。所以新代码如下所示:

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

并且英文会这样写“Honda Accord is a Car 支持充气轮胎和加油”。


虽然这个答案有其自身的优点,但它的大部分结论都取决于语言。例如,C++/hack 允许多重继承,并且接口和抽象类之间的区别只是按照惯例:可以通过放置抽象类来替换所有接口
P
Pradeep atkari

关键点:

抽象类可以同时具有属性、数据字段、方法(完整/不完整)。

如果在派生类中必须覆盖的抽象关键字中定义方法或属性。(它作为紧密耦合的功能工作)

如果为抽象类中的方法或属性定义抽象关键字,则不能定义方法主体和获取/设置属性值,并且必须在派生类中覆盖。

抽象类不支持多重继承。

抽象类包含构造函数。

抽象类可以包含子类、函数、属性的访问修饰符。

只有抽象类的完全成员可以是静态的。

一个接口只能从另一个接口继承,不能从抽象类继承,而抽象类可以从另一个抽象类或另一个接口继承。

优势:

这是一种强制所有子类继承相同层次结构或标准的契约。

如果各种实现属于同一类型并使用共同的行为或状态,那么最好使用抽象类。

如果我们向抽象类添加新方法,那么我们可以选择提供默认实现,因此所有现有代码都可以正常工作。

它允许比接口快速执行。(接口需要更多时间才能在相应的类中找到实际方法。)

它可以用于紧耦合和松耦合。

在此处查找详细信息... http://pradeepatkari.wordpress.com/2014/11/20/interface-and-abstract-class-in-c-oops/


J
Justin Time - Reinstate Monica

总结它的最短方法是 interface 是:

完全抽象,除了默认和静态方法;虽然它具有默认和静态方法的定义(方法签名 + 实现),但它只有其他方法的声明(方法签名)。遵循比类更宽松的规则(一个类可以实现多个接口,一个接口可以从多个接口继承)。所有变量都是隐式常量,无论是否指定为 public static final 。所有成员都是隐式公开的,无论是否指定。通常用作保证实现类将具有指定的功能和/或与实现相同接口的任何其他类兼容。

同时,abstract 类是:

从完全抽象到完全实现的任何地方,倾向于拥有一个或多个抽象方法。可以包含声明和定义,声明标记为抽象。一个成熟的类,并受制于管理其他类的规则(只能从一个类继承),条件是它不能被实例化(因为不能保证它被完全实现)。可以有非常量成员变量。可以实现成员访问控制,将成员限制为受保护、私有或私有包(未指定)。通常用于提供尽可能多的实现,以供多个子类共享,或者提供程序员能够提供的尽可能多的实现。

或者,如果我们想将其归结为一句话:interface 是实现类 所具有的,而 abstract 类是子类 所具有的


s
supercat

继承有两个用途:

允许对象将父类型数据成员和方法实现视为自己的。

允许希望引用超类型对象的代码使用对一种类型对象的引用。

在支持广义多重继承的语言/框架中,通常很少需要将类型分类为“接口”或“抽象类”。然而,流行的语言和框架将允许一种类型将另一种类型的数据成员或方法实现视为自己的,即使它们允许一种类型可以替代任意数量的其他类型。

抽象类可能具有数据成员和方法实现,但只能由不从任何其他类继承的类继承。接口对实现它们的类型几乎没有任何限制,但不能包含任何数据成员或方法实现。

有时类型可以替代许多不同的东西很有用。在其他时候,对象将父类型数据成员和方法实现视为自己的是有用的。区分接口和抽象类允许在最相关的情况下使用这些功能中的每一个。


S
Sheo Dayal Singh

抽象类和接口的区别代表真正的实现。

接口:是一个关键字,用于定义一个对象的模板或蓝图,它强制所有子类都遵循相同的原型,至于实现,所有子类都可以按照自己的方式自由实现功能这是要求。

我们应该使用接口的其他一些用例。

两个外部对象之间的通信(我们应用程序中的第三方集成)在这里通过接口完成接口作为合同。

抽象类:抽象,它是一个关键字,当我们在任何类之前使用这个关键字时,它就会变成抽象类。它主要用于我们需要定义模板以及对象的一些默认功能,然后是所有的子类,这样它删除了冗余代码和一个我们可以使用抽象类的用例,例如我们不希望其他类可以直接实例化该类的对象,只有派生类可以使用该功能。

抽象类的例子:

 public abstract class DesireCar
  {

 //It is an abstract method that defines the prototype.
     public abstract void Color();

  // It is a default implementation of a Wheel method as all the desire cars have the same no. of wheels.   
 // and hence no need to define this in all the sub classes in this way it saves the code duplicasy     

  public void Wheel() {          

               Console.WriteLine("Car has four wheel");
                }
           }


    **Here is the sub classes:**

     public class DesireCar1 : DesireCar
        {
            public override void Color()
            {
                Console.WriteLine("This is a red color Desire car");
            }
        }

        public class DesireCar2 : DesireCar
        {
            public override void Color()
            {
                Console.WriteLine("This is a red white Desire car");
            }
        }

接口示例:

  public interface IShape
        {
          // Defines the prototype(template) 
            void Draw();
        }


  // All the sub classes follow the same template but implementation can be different.

    public class Circle : IShape
    {
        public void Draw()
        {
            Console.WriteLine("This is a Circle");
        }
    }

    public class Rectangle : IShape
    {
        public void Draw()
        {
            Console.WriteLine("This is a Rectangle");
        }
    }

T
Toothless

我想再添加一个有意义的区别。例如,您有一个包含数千行代码的框架。现在,如果您想使用方法enhancedUI() 在整个代码中添加新功能,那么最好将该方法添加到抽象类而不是接口中。因为,如果您在接口中添加此方法,那么您应该在所有已实现的类中实现它,但如果您在抽象类中添加该方法,则情况并非如此。


u
user3775501

为了给出一个简单而明确的答案,设置上下文会有所帮助:当您不想提供完整的实现时,您可以同时使用两者。

主要区别在于接口根本没有实现(只有没有主体的方法),而抽象类也可以有成员和带有主体的方法,即可以部分实现。


由于您刚刚回答了它,因此您的回答没有考虑 Java 8 中的 default 关键字,您也可以使用它在接口中定义具体方法。
正如我所说,这对于处于学习差异的阶段的人来说是一个“简单但清晰的答案”。对于这样的人,知道这种异常没有任何好处,只会非常混乱。
T
Tutu Kumari

抽象类是不能创建对象的类或不能实例化的类。抽象方法使类抽象。需要继承抽象类才能覆盖抽象类中声明的方法。对访问说明符没有限制。抽象类中可以有构造函数和其他具体(非抽象方法)方法,但接口不能有。

接口是方法的蓝图/模板。(例如,给出了纸上的房子(接口房子),不同的建筑师将使用他们的想法来建造它(实现房子接口的建筑师类)。它是一个集合抽象方法、默认方法、静态方法、最终变量和嵌套类。所有成员将是 final 或 public ,不允许使用受保护和私有访问说明符。不允许创建对象。必须创建一个类才能使用实现接口并重写接口中声明的抽象方法。接口是松散耦合的一个很好的例子(动态多态/动态绑定)接口实现多态和抽象。它告诉做什么,但如何做由实现类。例如,有一家汽车公司,它希望其制造的所有汽车的某些功能都相同,因此该公司将制造具有这些功能的接口车辆特性和不同类别的汽车(如 Maruti Suzkhi、Maruti 800)将覆盖这些特性(功能)。

既然我们已经有了抽象类,为什么还要接口呢? Java 仅支持多级和分层继承,但在接口的帮助下,我们可以实现多重继承。


P
Peter Mortensen

在一个接口中,所有的方法都必须是唯一的定义,而不是一个应该实现的。

但是在抽象类中必须有一个只有定义的抽象方法,但其他方法也可以在具有实现的抽象类中......


M
MRMF

通常抽象类用于核心,但接口用于附加外围设备。

当你想为车辆创建基本类型时,你应该使用抽象类,但是如果你想添加一些不属于车辆基本概念的功能或属性,你应该使用接口,例如你想添加“ToJSON()”函数.

接口具有广泛的抽象而不是抽象类。你可以在传递参数中看到这一点。看这个例子:

https://i.stack.imgur.com/3kpBI.jpg

如果您使用车辆作为参数,则可以使用其派生类型之一(公共汽车或汽车-相同类别-仅车辆类别)。但是当您使用 IMoveable 接口作为参数时,您有更多选择。


T
Tokala Sai Teja

在实用性方面(JAVA),抽象类和接口之间的主要区别是抽象类可以保持状态。除了保持状态,我们还可以通过接口实现休息操作。