Java的“多重担任”
接口只是比抽象类“更纯”的一种形式。它的用途并不止那些。由于接口基础没有详细的实施细节——也就是说,没有与存储空间与“接口”关联在一起——所以没有任何步伐可以防备多个接口归并到一起。这一点是至关重要的,因为我们常常都需要表达这样一个意思:“x从属于a,也从属于b,也从属于c”。在C++中,将多个类归并到一起的动作称作“多重担任”,并且操纵较为未便,因为每个类都大概有一套本身的实施细节。在Java中,我们可采纳同样的动作,但只有个中一个类拥有详细的实施细节。所以在归并多个接口的时候,C++的问题不会在Java中重演。如下所示:
在一个衍生类中,我们并不必然要拥有一个抽象或详细(没有抽象要领)的基本类。假如确实想从一个非接口担任,那么只能从一个担任。剩余的所有根基元素都必需是“接口”。我们将所有接口名置于implements要害字的后头,并用逗号脱离它们。可按照需要利用多个接口,并且每个接口城市成为一个独立的范例,可对其举办上溯造型。下面这个例子展示了一个“详细”类同几个接口归并的环境,它最终生成了一个新类:
//: Adventure.java // Multiple interfaces import java.util.*; 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 { static void t(CanFight x) { x.fight(); } static void u(CanSwim x) { x.swim(); } static void v(CanFly x) { x.fly(); } static void w(ActionCharacter x) { x.fight(); } public static void main(String[] args) { Hero i = new Hero(); t(i); // Treat it as a CanFight u(i); // Treat it as a CanSwim v(i); // Treat it as a CanFly w(i); // Treat it as an ActionCharacter } } ///:~
从中可以看到,Hero将详细类ActionCharacter同接口CanFight,CanSwim以及CanFly归并起来。按这种形式归并一个详细类与接口的时候,详细类必需首先呈现,然后才是接口(不然编译器会报错)。
请留意fight()的签名在CanFight接口与ActionCharacter类中是沟通的,并且没有在Hero中为fight()提供一个详细的界说。接口的法则是:我们可以从它担任(稍后就会看到),但这样获得的将是另一个接口。假如想建设新范例的一个工具,它就必需是已提供所有界说的一个类。尽量Hero没有为fight()明晰地提供一个界说,但界说是伴同ActionCharacter来的,所以这个界说会自动提供,我们可以建设Hero的工具。
在类Adventure中,我们可看到共有四个要领,它们将差异的接口和详细类作为本身的自变量利用。建设一个Hero工具后,它可以通报给这些要领中的任何一个。这意味着它们会依次上溯造型到每一个接口。由于接口是用Java设计的,所以这样做不会有任何问题,并且措施员不必对此加以任何出格的存眷。
留意上述例子已向我们展现了接口最要害的浸染,也是利用接口最重要的一个原因:能上溯造型至多个基本类。利用接口的第二个原因与利用抽象基本类的原因是一样的:防备客户措施员建造这个类的一个工具,以及划定它仅仅是一个接口。这样便带来了一个问题:到底应该利用一个接口照旧一个抽象类呢?若利用接口,我们可以同时得到抽象类以及接口的长处。所以如果想建设的基本类没有任何要领界说可能成员变量,那么无论如何都愿意利用接口,而不要选择抽象类。事实上,假如事先知道某种对象会成为基本类,那么第一个选择就是把它酿成一个接口。只有在必需利用要领界说可能成员变量的时候,才应思量回收抽象类。