为什么要使用接口,多重继承对接口,接口的好处?

我对这件事还有些疑惑,我到现在为止的发现是

(类似的问题已经在这里提出,但我还有一些其他观点。)

  1. 接口是 ONLY 抽象方法和 final 字段的集合。

  2. 爪哇没有多重继承。

  3. 接口可以用来在 Java 中实现多重继承。

  4. 继承的一个优点是我们可以在派生类中使用基类的代码而不需要再次编写它。也许这是遗产存在的最重要的事情。

现在。

问题1。由于接口只有抽象方法(没有代码) ,所以我们怎么能说,如果我们实现任何接口,那么它是继承?我们不用它的代码。

如果实现接口不是继承,那么如何使用接口来实现多重继承?

问3。无论如何,使用接口的好处是什么?他们没有任何代码。我们需要在实现它的所有类中反复编写代码。

那么为什么要创建接口呢?

注意: 我发现了一种接口有帮助的情况。例如,在 Runnable 接口中,我们有一个 public void run ()方法,在这个方法中我们定义了线程的功能,并且在编码中内置了这个方法将作为一个单独的线程运行。所以我们只需要在线程中编写代码,Rest 是预定义的。但是这也可以通过使用抽象类来实现。

那么使用接口的确切好处是什么呢? 我们使用接口实现的真的是多继承吗?

78374 次浏览

问题1。由于接口只有抽象方法(没有代码) ,所以我们怎么能说,如果我们实现任何接口,那么它是继承?我们不用它的代码。

我们不能。接口不是用来实现多重继承的。他们用更安全的结构取而代之,虽然威力稍逊一筹。注意关键字 implements而不是 extends

如果实现接口不是继承,那么如何使用接口来实现多重继承?

他们不是。对于接口,一个类可以有多个“ 观点”、不同的 API 或功能。例如。一个类可以同时是 RunnableCallable,而这两个方法实际上是在做同样的事情。

问3。无论如何,使用接口的好处是什么?他们没有任何代码。我们需要在实现它的所有类中反复编写代码。

接口是一种多重继承,没有后者引入的问题(如 钻石问题)。

接口的用例很少:

  1. 对象实际上有两个恒等式: Tank VehicleWeapon。您可以使用 Tank的一个实例,其中预期使用前者或后者(多态性)。这种情况在现实生活中很少出现,实际上这是一个有效的例子,说明多重继承更好(或者说更优秀)。

  2. 简单的职责: 一个实例的 Tank对象在一个游戏中也是 Runnable,让您执行它在一个线程和一个 ActionListener,以响应鼠标事件。

  3. 回调接口: 如果对象实现了给定的回调接口,就会被通知其生命周期或其他事件。

  4. 标记接口: 不添加任何方法,但可以通过 instanceof轻松访问,以发现对象功能或愿望。SerializableCloneable就是这样的例子。

您正在寻找的是 trait (就像在 Scala 中一样) ,不幸的是在 Java 中不可用。

继承是指一个类从另一个类(可以是抽象的)或接口派生。面向对象(继承)的最大优点不是代码的重用(有很多方法可以做到这一点) ,而是多态性。

多态性是当您有使用接口的代码时,它的实例对象可以是从该接口派生的任何类。例如,我可以有这样一种方法: Public void Pet (IAnimal Animal) ,这个方法将获得一个对象,它是从 IAnimal 继承的 Dog 或 Cat 的实例。或者我可以有这样一个密码: 我是动物 然后我可以调用这个接口的一个方法: 吃哪只狗或猫可以用不同的方法实现。

接口的主要优点是可以从其中一些接口继承,但是如果只需要从一个接口继承,也可以使用抽象类。下面这篇文章详细解释了抽象类和接口之间的区别: Http://www.codeproject.com/kb/cs/abstractsvsinterfaces.aspx

问题1。由于接口只有抽象方法(没有代码) ,所以我们怎么能说,如果我们实现任何接口,那么它是继承?我们不用它的代码。

不幸的是,在口语用法中,当一个类实现一个接口时,仍然经常使用 inheritance这个词,尽管 interface implementation将是一个更好的术语—— IMO,术语 inheritance应该严格地与具体或抽象类的继承一起使用。在 C + + 和 C # 这样的语言中,类继承和接口实现都使用了相同的语法(即 Subclass : SuperclassClass : Interface) ,这可能导致了在接口中滥用 inheritance这个词的蔓延。对于 extending 类和 implementing 接口,Java 有不同的语法,这是一件好事。

如果实现接口不是继承,那么如何使用接口来实现多重继承?

你可以通过组合实现多重继承的“效果”——在一个类上实现多个接口,然后为类上所有接口所需的所有方法、属性和事件提供实现。对具体类进行这种操作的一种常见技术是通过与实现外部接口的类进行“ has-a”(组合)关系,这些类将实现“连接”到每个内部类实现。(像 C + + 这样的语言确实直接支持多重具体继承,但是这会产生其他潜在的问题,比如钻石问题)。

Q3无论如何,使用接口的好处是什么?他们没有任何代码。我们需要在实现它的所有类中反复编写代码。

接口允许现有的类(例如框架)与您的新类交互,而之前从未“见过”它们,因为它们能够通过已知的接口进行通信。把接口想象成一个契约。通过在类上实现这个接口,您在契约上必须满足它所要求的义务,一旦实现了这个契约,那么您的类应该能够与使用该接口的任何其他代码交换使用。

真实世界的例子

一个“真实世界”的例子就是某个国家围绕电气墙插座的立法和惯例(接口)。插入插座的电器须符合当局为插座订定的规格(合约) ,例如线路的位置、中性线及接地线的位置、开关的位置及颜色,以及开启时透过 interface提供的电压、频率及最大电流的符合程度。

解耦接口的好处(比如一个标准的墙上插座) ,而不仅仅是焊接电线在一起,你可以插入(和拔出)一个风扇,一个水壶,一个双适配器,或一些新的设备发明的明年,即使这个设备不存在时,接口的设计。为什么?因为它将 顺从的接口要求。

为什么要使用接口?

接口非常适合类的松散耦合,并且是 Bob 叔叔的 很可靠范例的主体之一,特别是 Dependency Inversion PrincipleInterface Segregation Principles

简单地说,通过确保类之间的依赖关系只耦合在接口(抽象)上,而不耦合在其他具体的类上,它允许用满足接口要求的任何其他类实现替换依赖关系。

在测试中,可以使用依赖项的存根和模拟来对每个类进行单元测试,并且可以“监视”类与依赖项之间的交互。

接口是最终静态字段和抽象方法的集合(NewlyJava8增加了在接口中使用静态方法的支持)。

接口是在我们知道必须完成某些任务的情况下创建的,但是应该如何完成这些任务可能会有所不同。换句话说,我们可以说我们实现了接口,所以我们的类开始以特定的方式运行。

让我用一个例子来解释,我们都知道动物是什么。就像狮子是动物,猴子是动物,大象是动物,牛是动物等等。现在我们知道所有的动物吃东西和睡觉。但是每种动物吃东西或睡觉的方式可能不同。就像狮子捕食其他动物一样,牛吃草。但是两个都要吃。我们可以用这样的伪代码,

interface Animal {
public void eat();
public void sleep();
}


class Lion implements Animal {
public void eat() {
// Lion's way to eat
}


public void sleep(){
// Lion's way to sleep
}
}


class Monkey implements Animal {
public void eat() {
// Monkey's way to eat
}


public void sleep() {
// Monkey's way to sleep
}
}

根据上面提到的伪代码,任何能够吃或睡的东西都将被称为动物,或者我们可以说所有动物都必须吃和睡,但是吃和睡的方式取决于动物。

对于接口,我们只继承行为,而不像对于类的继承那样继承实际代码。

问题1。由于接口只有抽象方法(没有代码) ,所以我们怎么能说,如果我们实现任何接口,那么它是继承?我们不用它的代码。

实现接口是另一种继承。它与继承类的继承不同,因为继承子类从基类获得重用的实际代码。

如果实现接口不是继承,那么如何使用接口来实现多重继承?

之所以这么说是因为一个类可以实现多个接口。但是我们需要理解,这种继承与类的继承是不同的。

问3。无论如何,使用接口的好处是什么?他们没有任何代码。我们需要在实现它的所有类中反复编写代码。

实现接口会强迫类必须重写其所有抽象方法。

在我的书 给你给你中阅读更多

亲一个

我已经搜索了好几天,甚至好几个星期,试图理解界面,并且似乎读到了同样的通用帮助; 我并不是想贬低这些贡献,但我觉得灵光一现,所以我很高兴:)

我更喜欢保持简单的愚蠢,所以将提供我新发现的接口视图。

我是一个临时编码员,但我想发布这个代码,我写在 VB.NET (原则是相同的其他语言) ,以帮助其他人了解接口。

如果我有错误,那么请让其他人知道在后续评论。

解释

在一个窗体上单击三个按钮,每个按钮保存对接口变量(_ data)的不同类引用。对接口变量的不同类引用的全部意义,是我不理解的,因为它看起来是冗余的,然后它的力量在 msgbox 中变得明显,我只需要调用 SAME 方法来执行我需要的任务,在这个例子中是‘ GetData ()’,它使用当前由接口引用变量(_ data)保存的类中的方法。

因此,无论我希望得到我的数据(从数据库,网络或文本文件) ,它只是曾经做过使用 相同的方法名; 代码背后的实现... 我不关心。

然后就可以轻松地使用接口更改每个类代码,而不需要任何依赖关系... ... 这是面向对象和封装的一个关键目标。

何时使用

编写类的代码,如果注意到方法使用了相同的谓词,比如‘ GetData ()’,那么最好在该类上实现一个接口,并使用该方法名作为抽象/接口。

我真诚地希望这个原则能帮助一个菜鸟同胞。

Public Class Form1


Private _data As IData = Nothing


Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
_data = New DataText()
MsgBox(_data.GetData())
End Sub


Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
_data = New DataDB()
MsgBox(_data.GetData())
End Sub


Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
_data = New DataWeb()
MsgBox(_data.GetData())
End Sub


End Class


Public Interface IData
Function GetData() As String
End Interface


Friend Class DataText : Implements IData


Friend Function GetData() As String Implements IData.GetData
Return "DataText"
End Function


End Class


Friend Class DataDB : Implements IData


Friend Function GetData() As String Implements IData.GetData
Return "DataDB"
End Function


End Class


Friend Class DataWeb : Implements IData


Friend Function GetData() As String Implements IData.GetData
Return "DataWeb"
End Function


End Class

创建接口是为了让类在接口中实现功能,并根据该接口进行操作。

这是一个很老的问题,java-8的发布增加了更多的功能和功能。

接口声明可以包含

  1. 方法签名
  2. 默认方法
  3. 静态方法
  4. 不变的定义。

接口中具有实现的唯一方法是 违约静电干扰方法。

界面的用途:

  1. 定义 合约
  2. 将不相关的类链接到具有 能力的类(例如,除了实现该接口之外,实现 Serialable 接口的类之间可能有也可能没有任何关系
  3. 提供 可以互换实现,例如 Strategy_pattern
  4. Default 方法使您能够向库的接口添加新功能,并确保使用为这些接口的旧版本编写的代码编写 二进制兼容性
  5. 使用 静电干扰方法组织库中的 helper 方法(可以将特定于接口的静态方法保存在同一个接口中,而不是单独的类中)

为了更好地理解这些概念,请看一下这个相关的 SE 问题的代码示例:

我应该如何解释接口和抽象类之间的区别?

回到你的问题:

问题1。由于接口只有抽象方法(没有代码) ,所以我们怎么能说,如果我们实现任何接口,那么它是继承?我们不用它的代码。

如果实现接口不是继承,那么如何使用接口来实现多重继承?

接口可以包含 静电干扰违约方法的代码。这些默认方法提供了向下兼容和静态方法提供了 Helper/实用程序函数。

你不可能在 java 中拥有真正的多重继承,而且接口也不是获得它的方法。接口只能包含常量。所以你不能继承状态,但是你可以实现行为。

你可以用 能力.接口为实现类提供了多种功能。代替 继承遗产

问3。无论如何,使用接口的好处是什么?他们没有任何代码。我们需要在实现它的所有类中反复编写代码。

请参考我答案中的“ 接口的用途”部分。

接口

接口是定义如何与对象交互的契约。它们有助于表达您的内部结构打算如何与对象进行交互。在 依赖性反转之后,您的公共 API 将具有用接口表示的所有参数。你不在乎它如何做你需要它做的事,只要它能做你需要它做的事。

例如: 你可能只是需要一个 Vehicle来运输货物,你并不关心特定的运输方式。

遗产

继承是特定实现的扩展。该实现可能满足也可能不满足特定的接口。只有在您关心如何实现的时候,才应该期望特定实现的祖先。

示例: 您可能需要一个用于快速运输的车辆的 Plane实现。

作曲

组合可以作为继承的一种替代方法。与扩展基类的类不同,它是使用实现主类职责的较小部分的对象创建的。组合在 facade patterndecorator pattern中使用。

示例: 您可以创建一个实现 LandVehicleWaterVehicleDuckBoat(DUKW)类,它们都实现由 TruckBoat实现组成的 Vehicle

答案

问题1。由于接口只有抽象方法(没有代码) ,所以我们怎么能说,如果我们实现任何接口,那么它是继承?我们不用它的代码。

接口不是继承。实现接口表示您希望类按照接口定义的方式进行操作。继承是指您有一个共同的祖先,并且您接收到与该祖先相同的行为(inherit) ,因此不需要定义它。

如果实现接口不是继承,那么如何使用接口来实现多重继承?

接口不能实现多重继承,它们表示一个类可能适合多个角色。

问3。无论如何,使用接口的好处是什么?他们没有任何代码。我们需要在实现它的所有类中反复编写代码。

接口的一个主要好处是提供关注点分离:

  • 您可以编写一个使用另一个类执行某些操作的类,而不必关心该类是如何实现的。
  • 任何未来的开发都可以与您的实现兼容,而无需扩展特定的基类。

按照 DRY的精神,您可以编写一个满足接口要求的实现,并对其进行更改,同时如果您利用组合,则仍然尊重 open/closed principal

问题1。由于接口只有抽象方法(没有代码) ,所以我们怎么能说,如果我们实现一个接口,那么它是继承?我们不用它的代码。

这不是平等的继承,只是相似而已,让我解释一下:

VolvoV3 extends VolvoV2, and VolvoV2 extends    Volvo (Class)
VolvoV3 extends VolvoV2, and VolvoV2 implements Volvo (Interface)


line1: Volvo v = new VolvoV2();
line2: Volvo v = new VolvoV3();

如果您只看到 line1和 line2,则可以推断 VolvoV2和 VolvoV3具有相同的类型。你无法推断沃尔沃是一个超类还是一个接口。

如果实现一个接口不是继承,那么如何使用接口来实现多重继承?

现在使用接口:

VolvoXC90 implements XCModel and Volvo (Interface)
VolvoXC95 implements XCModel and Volvo (Interface)


line1: Volvo   a = new VolvoXC90();
line2: Volvo   a = new VolvoXC95();
line3: XCModel a = new VolvoXC95();

如果只看到 line1和 line2,则可以推断 VolvoXC90和 VolvoXC95具有相同的类型(Volvo)。你不能推断沃尔沃是一个超类或沃尔沃是一个接口。

如果您只看到 line2和 line3,您可以推断 Volvo95实现了两种类型,XCModel 和 Volvo,在 Java 中,您知道至少有一个必须是接口。例如,如果这段代码是用 C + + 编写的,那么它们可以同时是两个类。因此,多重继承。

问3。无论如何,使用接口的好处是什么?他们没有任何代码。我们需要在实现它的所有类中反复编写代码。

想象一个系统,您在其他200个类中使用 VolvoXC90类。

VolvoXC90 v = new VolvoXC90();

如果您需要改进系统以启动 VolvoXC95,则必须更改200个其他类。

现在,想象一个系统,在10,000,000个类中使用 Volvo 接口。

// Create VolvoXC90 but now we need to create VolvoXC95
Volvo v = new VolvoFactory().newCurrentVolvoModel();

现在,如果您需要改进您的系统以创建 VolvoXC95模型,那么您只需要更改一个类,Factory。

这是一个常识性的问题。如果您的系统只由几个类组成,并且只有很少的更新,那么在任何地方使用接口都会适得其反。对于大型系统,它可以为您节省很多麻烦,并避免采用接口的风险。

我建议你多读一些关于 S.O.L.I.D 原则的书,还有《有效的 Java 》这本书。它从经验丰富的软件工程师那里得到了很好的教训。

两种方法都可以工作(接口和多重继承)。

快速实用简答

如果你有多年使用超级类的经验,只有方法定义,没有任何代码,那么多重继承接口会更好。

一个补充问题可能是: “如何以及为什么要将代码从抽象类迁移到接口”。

如果您在应用程序中没有使用很多抽象类,或者您没有很多使用它的经验,那么您可能更愿意跳过接口。

不要急于使用接口。

冗长乏味的回答

接口非常相似,甚至等价于抽象类。

如果您的代码有许多抽象类,那么您就应该开始从接口的角度考虑问题了。

下面的代码包含抽象类:


MyStreamsClasses.java

/* File name : MyStreamsClasses.java */
import java.lang.*;
// Any number of import statements


public abstract class InputStream {
public void ReadObject(Object MyObject);
}


public abstract class OutputStream {
public void WriteObject(Object MyObject);
}


public abstract class InputOutputStream
imnplements InputStream, OutputStream {
public void DoSomethingElse();
}

可以替换为:


MyStreamsInterfaces.java

/* File name : MyStreamsInterfaces.java */
import java.lang.*;
// Any number of import statements


public interface InputStream {
public void ReadObject(Object MyObject);
}


public interface OutputStream {
public void WriteObject(Object MyObject);
}


public interface InputOutputStream
extends InputStream, OutputStream {
public void DoSomethingElse();
}

干杯。

老问题了。令我惊讶的是,没有人引用经典的资料来源: 詹姆斯 · 高斯林的 Java: 概述,四人帮的 设计模式: 可重用面向对象软件的元素或乔舒亚 · 布洛赫的 有效的爪哇(还有其他资料)。

我先引用一句话:

接口仅仅是对象响应的一组方法的规范。它不包括任何实例变量或实现。接口可以是多重继承的(与类不同) ,并且它们可以以比通常的刚性类更灵活的方式使用 继承结构。(高斯林,第8页)

现在,让我们一个接一个地回答您的假设和问题(我自愿忽略 Java8的特性)。

假设

接口是 ONLY 抽象方法和 final 字段的集合。

您在 Java 接口中看到关键字 abstract了吗?没有。那么您就不应该将接口看作是抽象方法的集合。也许您被 C + + 所谓的接口误导了,这些接口是只有纯虚方法的类。根据设计,C + + 没有(也不需要)接口,因为它具有多重继承性。

正如 Gosling 所解释的,您应该将接口视为“对象响应的一组方法”。我希望看到 及相关文件接口作为服务契约。它描述了您可以期望从实现该接口的对象中得到什么。文档应该指定前置和后置条件(例如,参数不应该为空,输出总是正的,...)和不变量(不修改对象内部状态的方法)。我认为这个合同是 OOP 的核心。

爪哇没有多重继承。

确实。

JAVA 忽略了 C + + 中许多很少使用、理解不透彻、令人困惑的特性,在我们的经验中,这些特性带来的痛苦多于好处。这主要包括运算符重载(尽管它确实有方法过载)、多重继承和广泛的自动胁迫。(高斯林,第2页)

没什么好补充的。

接口可以用来在 Java 中实现多重继承。

不,因为在 Java 中没有多重继承,见上文。

继承的一个优点是我们可以在派生类中使用基类的代码而不需要再次编写它。也许这是遗产存在的最重要的事情。

这就是所谓的“实现继承”。

但它有一个重要的对应物:

父类通常至少定义其子类物理表示的一部分。因为继承将子类暴露给其父类实现的细节,所以人们常说“继承破坏封装”[ Sny86]。子类的实现与其父类的实现紧密相连,以至于父类实现中的任何更改都将迫使子类发生更改。(GOF,1.6)

(Bloch 也有类似的报价,第16项。)

实际上,继承还有另一个目的:

类继承结合了接口继承和实现继承。接口继承定义了一个新的接口 或更多现有的接口。实现继承根据一个或多个现有实现定义新实现。(GOF,附录 A)

两者在 Java 中都使用关键字 extends。您可能有类的层次结构和接口的层次结构。前者共同执行,后者共同承担义务。

问题

问题1。由于接口只有抽象方法(没有代码) ,所以我们怎么能说,如果我们实现任何接口,那么它是继承?我们不用它的代码。**

接口的实现不是继承,而是实现。

如果实现接口不是继承,那么如何使用接口来实现多重继承? * *

在爪哇没有多重继承,见上文。

问3。无论如何,使用接口的好处是什么?他们没有任何代码。我们需要在实现它的所有类中反复编写代码。/那么为什么要创建接口呢?/使用接口的确切好处是什么?我们使用接口实现的真的是多继承吗?

最重要的问题是: 你为什么想要多重继承?我能想到两个答案: 1。给一个对象赋予多种类型;。重用代码。

为对象提供多种类型

在 OOP 中,一个对象可能有 Serializable4。例如在 Java 中,ArrayList<E>有以下类型: SerializableCloneableIterable<E>Collection<E>List<E>RandomAccessAbstractList<E>AbstractCollection<E>Object(我希望我没有忘记任何人)。如果一个对象具有不同的类型,各种使用者将能够在不知道其特定性的情况下使用它。我需要一个 Iterable<E>,你给我一个 ArrayList<E>?没事的。但是如果我现在需要一个 List<E>,你给我一个 ArrayList<E>,也没关系。等等。

如何在 OOP 中键入对象?您以 Runnable接口为例,这个例子很好地解释了这个问题的答案。我引用官方的 Java 文档:

此外,Runnable 还提供了类在不对 Thread 子类化的情况下处于活动状态的方法。

重点是: 继承是输入 < em > 对象的一种方便方法。你想创建一个线程?让我们继承 Thread类。您希望一个对象具有不同的类型,让我们使用多重继承。啊。Java 里没有。(在 C + + 中,如果希望对象具有不同的类型,可以采用多重继承。)

那么如何给一个对象赋予多种类型呢?在 Java 中,可以键入对象 直接。这就是类 implementsRunnable接口时要做的。如果你喜欢继承,为什么要使用 Runnable?可能是因为您的类已经是另一个类的子类,比如说 A。现在您的类有两种类型: ARunnable

使用多个接口,您可以为一个对象提供多种类型。您只需要创建一个 implements多个接口的类。只要你遵守合同,就没问题。

重用代码

这是一个困难的主题; 我已经引用了关于破解封装的 GOF。另一个答案提到了钻石问题。你也可以想想单一责任原则:

一个类应该只有一个改变的理由。(罗伯特 · C · 马丁,敏捷软件开发,原则,模式和实践)

拥有一个父类可能会给一个类一个改变的理由,除了它自己的责任:

超类的实现可能会在不同的版本间变化,如果变化了,子类可能会中断,即使它的代码没有被触及。因此,子类必须与其超类一起进化(Bloch,第16项)。

我想添加一个更平凡的问题: 当我试图在一个类中找到一个方法的源代码但是找不到它时,我总是有一种奇怪的感觉。然后我想起来了: 它必须在父类的某个地方定义。或者在祖父母班。或者更高。在这种情况下,一个好的 IDE 是一种有价值的资产,但在我看来,它仍然是一种神奇的东西。与接口的层次结构完全不同,因为 javadoc 是我唯一需要的东西: IDE 中的一个快捷键,我就能得到它。

继承少有好处:

在包中使用继承是安全的,其中子类和超类的实现由相同的程序员控制。在扩展专门为扩展而设计和记录的类时,使用继承也是安全的(第17项: 为继承而设计和记录,否则禁止继承)。(Bloch,第16项)

在 Java 中“专门为扩展而设计和记录”的类的一个例子是 AbstractList

但布洛赫和 GOF 坚持认为: “赞成组合优于继承”:

委托是一种使复合与继承一样强大的重用方式[ Lie86,JZ91]。在委托中,处理请求涉及两个对象: 接收对象将操作委托给其委托。这类似于将请求推迟到父类的子类。(GOF p. 32)

如果使用组合,就不必一次又一次地编写相同的代码。您只需创建一个处理重复的类,然后将该类的一个实例传递给实现接口的类。这是重用代码的一种非常简单的方法。这有助于您遵循单一责任原则,并使代码更具可测试性。Rust 和 Go 没有继承(它们也没有类) ,但是我不认为这些代码比其他 OOP 语言中的代码更多余。

此外,如果您使用组合,您会发现自己很自然地使用接口来为代码提供所需的结构和灵活性(请参阅关于接口用例的其他答案)。

注意: 您可以与 Java8接口共享代码

最后,最后一句话:

在令人难忘的问答环节中,有人问他(詹姆斯 · 高斯林) : “如果你可以重新使用 Java,你会改变什么?”“我会省略课程”(在网上的任何地方,不知道这是否是真的)

那么。这里有很多很好的答案,详细解释了什么是接口。然而,这是一个使用它的例子,我最好的一个同事多年前曾向我解释过这种方法,其中混合了我在过去几年在大学里学到的东西。

接口是一种“契约”。它公开了一些可用的方法、字段等等。它不显示任何实现细节,只显示它返回的内容和它接受的参数。这里有第三个问题的答案,我觉得这是现代 OOP 最大的优势之一:

“通过添加而不是修改的代码”-Magnus Madsen,AAU

至少他是这么说的,他可能是从别的地方拿到的。下面的示例代码是用 C # 编写的,但是所有显示的代码都可以用 Java 中的同样方法来完成。

我们看到的是一个名为 SampleApp 的类,它只有一个字段,IOContext.IOContext 是一个接口。 SampleApp 并不关心它如何保存数据,它只需要在它的“ doSomething ()”方法中这样做。

我们可以想象,保存数据可能比在开发过程开始时如何保存数据更重要,因此开发人员选择简单地编写 FileContext 类。然而,后来,不管出于什么原因,他需要支持 JSON。因此,他编写了 JSONFileContext 类,该类继承 FileContext。这意味着它实际上是一个 IOContext,它具有 FileContext 的功能,节省了 FileContext SaveData 和 LoadData 的替代,它仍然使用它的“写/读”方法。

与编写类相比,实现 JSON 类的工作量很小,而且它只是继承 IOContext。

SampleApp 的字段可能只是类型为‘ FileContext’,但是那样的话,它将被限制为只使用该类的子类。通过创建接口,我们甚至可以执行 SQLiteContext 实现,并写入数据库,SampleApp 永远不会知道或关心,当我们编写 SQL lite 类时,我们只需要对代码做一个更改: new JSONFileContext();变成 new SQLiteContext();

我们仍然有旧的实现,如果需要的话可以切换回来。我们没有破坏任何东西,所有对代码的更改都是半行,可以在眨眼之间修改回来。

So: 通过添加代码,而不是通过修改代码。

namespace Sample
{
class SampleApp
{
private IOContext context;


public SampleApp()
{
this.context = new JSONFileContext(); //or any of the other implementations
}


public void doSomething()
{
//This app can now use the context, completely agnostic of the actual implementation details.
object data = context.LoadData();
//manipulate data
context.SaveData(data);
}
}


interface IOContext
{
void SaveData(object data);
object LoadData();
}


class FileContext : IOContext
{
public object LoadData()
{


object data = null;
var fileContents = loadFileContents();
//Logic to turn fileContents into a data object
return data;
}


public void SaveData(object data)
{
//logic to create filecontents from 'data'
writeFileContents(string.Empty);
}


protected void writeFileContents(string fileContents)
{
//writes the fileContents to disk
}


protected string loadFileContents()
{
string fileContents = string.Empty;
//loads the fileContents and returns it as a string
return fileContents;
}
}


class JSONFileContext : FileContext
{
public new void SaveData(object data)
{
//logic to create filecontents from 'data'
base.writeFileContents(string.Empty);
}


public new object LoadData()
{
object data = null;
var fileContents = loadFileContents();
//Logic to turn fileContents into a data object
return data;
}
}


class SQLiteContext : IOContext
{
public object LoadData()
{
object data = null;
//logic to read data into the data object
return data;
}


public void SaveData(object data)
{
//logic to save the data object in the database
}
}
}