企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## [多接口结合](https://lingcoder.gitee.io/onjava8/#/book/10-Interfaces?id=%e5%a4%9a%e6%8e%a5%e5%8f%a3%e7%bb%93%e5%90%88) 接口没有任何实现——也就是说,没有任何与接口相关的存储——因此无法阻止结合的多接口。这是有价值的,因为你有时需要表示“一个**x**是一个**a**和一个**b**以及一个**c**”。 ![类图](https://lingcoder.gitee.io/onjava8/images/1562999314238.png) 派生类并不要求必须继承自抽象的或“具体的”(没有任何抽象方法)的基类。如果继承一个非接口的类,那么只能继承一个类,其余的基元素必须都是接口。需要将所有的接口名称置于**implements**关键字之后且用逗号分隔。可以有任意多个接口,并可以向上转型为每个接口,因为每个接口都是独立的类型。下例展示了一个由多个接口组合而成的具体类产生的新类: ~~~ // interfaces/Adventure.java // Multiple interfaces interface CanFight { void fight(); } interface CanSwim { void swim(); } interface CanFly { void fly(); } class ActionCharacter { public void fight(){} } class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly { public void swim() {} public void fly() {} } public class Adventure { public static void t(CanFight x) { x.fight(); } public static void u(CanSwim x) { x.swim(); } public static void v(CanFly x) { x.fly(); } public static void w(ActionCharacter x) { x.fight(); } public static void main(String[] args) { Hero h = new Hero(); t(h); // Treat it as a CanFight u(h); // Treat it as a CanSwim v(h); // Treat it as a CanFly w(h); // Treat it as an ActionCharacter } } ~~~ 类**Hero**结合了具体类**ActionCharacter**和接口**CanFight**、**CanSwim**和**CanFly**。当通过这种方式结合具体类和接口时,需要将具体类放在前面,后面跟着接口(否则编译器会报错)。 接口**CanFight**和类**ActionCharacter**中的`fight()`方法签名相同,而在类 Hero 中也没有提供`fight()`的定义。可以扩展一个接口,但是得到的是另一个接口。当想创建一个对象时,所有的定义必须首先都存在。类**Hero**中没有显式地提供`fight()`的定义,是由于该方法在类**ActionCharacter**中已经定义过,这样才使得创建**Hero**对象成为可能。 在类**Adventure**中可以看到四个方法,它们把不同的接口和具体类作为参数。当创建一个**Hero**对象时,它可以被传入这些方法中的任意一个,意味着它可以依次向上转型为每个接口。Java 中这种接口的设计方式,使得程序员不需要付出特别的努力。 记住,前面例子展示了使用接口的核心原因之一:为了能够向上转型为多个基类型(以及由此带来的灵活性)。然而,使用接口的第二个原因与使用抽象基类相同:防止客户端程序员创建这个类的对象,确保这仅仅只是一个接口。这带来了一个问题:应该使用接口还是抽象类呢?如果创建不带任何方法定义或成员变量的基类,就选择接口而不是抽象类。事实上,如果知道某事物是一个基类,可以考虑用接口实现它(这个主题在本章总结会再次讨论)。