Java为什么要让接口继承接口?

51

我想知道在什么情况下我们会从一个接口扩展出另一个接口?因为,例如

interface A{
    public void method1();
}
interface B extends A{
    public void method2();
}
class C implements B{
    @Override public void method1(){}
    @Override public void method2(){}
}

这不就相当于

interface A{
    public void method1();
}
interface B{
    public void method2();     
}
class C implements A, B{
    @Override public void method1(){}
    @Override public void method2(){}
}

是否有任何重要原因背后的原因?


9
在第一种情况下,类C的编码者不需要知道B继承了A的细节,他只需要阅读B的文档即可。 - Hot Licks
@peter 考虑选择一个“最佳答案”。 - Jac Frall
8个回答

78

重要的原因完全取决于接口的预期功能。

如果您有一个名为Vehicle的接口和一个名为Drivable的接口,则可以理解所有车辆都是可驾驶的。没有接口继承,每种不同类型的汽车类都需要

class ChevyVolt implements Vehicle, Drivable
class FordEscort implements Vehicle, Drivable
class ToyotaPrius implements Vehicle, Drivable

等等类似的。

就像我说的,所有的车都可以驾驶,所以只需要:

class ChevyVolt implements Vehicle
class FordEscort implements Vehicle
class ToyotaPrius implements Vehicle

以下是车辆:

interface Vehicle extends Drivable

而不必去考虑它。


3
如果在编码块中给车辆接口扩展可驾驶接口,对于新手来说会不会更好呢? - atiqkhaled

15

有的,这就像在其他地方一样。如果B 是 A 的一个特殊化,则应该这样写。第二个示例表明该类碰巧实现了两个没有任何关系的接口。

从最终结果的角度来看,您可以只使用多个接口来代替处理层次结构(正如您可以避免在类层次结构中使用继承行为)。然而,这会使信息更加分散,并且(如果不是更重要的话)它会混淆软件模型的意图。


A和B是“接口”。那么,当你说B是A时,这意味着什么?“接口”是在具体类设计之后发现的。我觉得扩展接口是由于糟糕的设计而产生的结果。如果你理解“继承”的含义,继承接口就没有意义。继承具体类是有意义的,因为它们代表或展示了真实世界对象的本质,你继承了那种本质,因为“子类型”也是那种本质的一部分。顺便问一下,在jdk中能否给我举一个扩展接口的例子?我想分析一下。 - overexchange
1
具体类对应于“真实世界对象”的实现,而接口表示它们的合同或定义应该“显示”什么。通常,如果您具有具有合同专业化的类层次结构,则相应的接口层次结构可能会有很多意义。我不会为您研究示例,但我敢打赌Collections API中充满了这些内容。 - Matt Whipple
所以,当有人说“public interface List extends Collection{}”时,你的意思是,“List”是“Iterable”,因为“List”是“Collection”,而“Collection”是“Iterable”吗?我认为这是错误的。请查看来自知道集合设计者的Oracle技术员的答案。基本上我的观点是,从设计角度来看,将一个“接口”扩展到另一个“接口”是没有意义的。 - overexchange
那个线程涵盖了一个你正在雄心勃勃地概括的特定实例。底层讨论是关于Java的单一实现继承的限制,这是整个对话的基础和原因,尽管使用抽象类和类似模板方法的方法可能更好,但它们不能普遍适用。仅仅刮掉一层并评估仅扩展Iterable更具代表性。我肯定会说接口层次结构不应该很深。 - Matt Whipple
很可能在jdk中还有其他的,集合只是很快就想到了。这里是另一个接口层次结构:https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executor.html。如果你真的去看,很可能还有很多需要澄清的内容,甚至还没有涉及到核心平台之外的标准框架和库。 - Matt Whipple
显示剩余3条评论

8

这个地方只有一个评论。

interface A{
    public void method1();
}
interface B extends A{
    public void method2();
}
class C implements B{
    @Override public void method1(){}
    @Override public void method2(){}
}

如果你声明 A a = new C();

那么你就不能调用 method2();,因为接口 A 对接口 B 中的元素一无所知,即使你在类 C 中实现了方法也不行。


这就是为什么要扩展接口的真正原因。如果你想实现一个适用于接口的通用过程,你不能在没有转换的情况下访问两个方法。 - cobby

7

是的,有一个很大的区别。

在您的第一个示例中,您定义了新接口B扩展自A,因此从现在开始,任何实现B的类都会自动实现A。基本上,您告诉编译器:“这就是成为A的意义,这就是成为B的意义,而且,顺便说一下,所有的B也都是A!”这使您可以说出这样的话...

class C implements B {
    ... you implement all of the methods you need...
    ...blah...
    ...blah...
}
A myNewA = new C();

在您的第一个例子中,您将B声明为扩展A的类,因此以下代码可以正常工作。

但是,在您的第二个示例中,您没有声明B扩展A,因此上面的代码将无法正常工作。当您尝试将(第二种类型的)C的实例分配到A的引用中时,它会抱怨,因为您没有告诉编译器“所有B实际上都是A”。(即B和C之间没有关系)


4
如果您不希望在未实现A的情况下实现B,可以通过让B继承A来实现。
例如:
interface LivingThing{
public void eat();
}

interface Dog extends LivingThing{
public void Bark();
}

一个生物不一定是狗,但一个狗一定是生物。因此,如果它能叫,它也能吃,但反之则不一定成立。


2
有时候,你不希望只有 A 而没有 B,但是想要允许只有 A 而不一定需要 B
如果 ACarBSportCar,你可能想要使用 Car 接口来处理一些流程,但有时需要更具体地使用 SportCar。这样,你就不能决定某天只实现 SportCar。它强制你也要实现 Car。这正是面向对象编程中继承的含义。
此外,假设你想要声明一个新的类成员来实现 SportCar。你如何使其也实现 Car 接口呢?据我所知,没有办法添加由多个接口组成的类型。
public SportCar myCar;
// public SportCar,Car myCar -> invalid

这样,你就不能保证myCar具有Car方法(如Drive()),这会失去从继承中获得的优势。

1

你所说的是正确的,但不仅仅是为了完成我们的工作,如果需求是这样的:

1)假设有10个接口,并非同时设计。例如 Java 7 中的 AutoCloseable 接口,添加了一个新的自动关闭功能,在 Java 6中不存在。

2)如果您设计了一个标记接口 C,并且希望从给定类 B 派生的所有类都被标记,则最好的解决方案是使用 B 扩展 C ,而不是去到处放置 implements C。

还有许多其他原因。如果您查看类和继承结构的大局,您可能会自己找到答案。

很高兴能够帮助 Dharam


0
主要的区别在于第一个例子中B是A,而且还有其他的。这意味着接口B调用method1()和method2(),而在第二个例子中,A和B是分开的(接口B不包括method1()),类C实现了A和B。在两种情况下,类C都将覆盖method1()和method2()。希望这可以帮到你!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接