interface CanFight{
void fight();
}
interface CanSwim{
void swim();
}
interface CanFly{
void fly();
}
class ActionCharacter{
public void fight(){
System.out.println("ActionCharacter.fight()");
}
}
class Hero extends ActionCharacter implements CanFight,CanSwim,CanFly{
public void swim(){
System.out.println("Hero.swim()");
}
public void fly(){
System.out.println("Hero.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);
u(h);
v(h);
w(h);
}
}
(1)可以看到Hero组合了具体类ActionCharacter和接口CanFight,CanSwim和CanFly,当通过这种方式将一个具体类和多个接口组合到一起时,这个具体类必须放在前面,后面跟着才是接口,否则编译器就会报错.
(2)注意CanFight接口与ActionCharacter类中的fight()方法的特征签名是一样的,而且,在Hero中并没有提供fight()的定义.接口的规则是:可以从接口中继承,但是得到的只是另一个接口.如果想创建该新类型的对象,就必须有一个提供了其全部定义的类.即使Hero没有显式地提供fight()的定义,其定义也因ActionCharacter而随之而来,因此它是被自动提供的,这使得创建Hero对象成为了可能.
(3)在Adventure类中,可以看到有四个方法把上述各种接口和具体类作为参数.当Hero对象被创建时,它可以被传递给这些方法中的任何一个,这意味着它依次被向上转型为每一个接口.由于Java中这种设计接口的方式,使得这项工作并不需要程序付出特别的努力.
(4)一定要记住,上面的例子所展示的就是使用接口的核心原因:为了能够向上转型为多个基类型.然而,使用接口的第二个原因却是与使用抽象基类相同:防止客户端程序员创建该类的对象,并确保这仅仅是一个接口.这就带来一个问题:我们应该使用接口还是抽象类呢?接口为我们带来了使用抽象类的好处,并且还带来了使用接口的好处,所以如果要创建不带任何方法定义和成员变量的类,那么就应该选择接口而不是抽象类.事实上,如果知道某事物应该成为一个基类,那么第一选择应该是使它成为一个接口,只有在强制你必须要具有方法定义和成员变量的时候,才应该改为选择抽象类,或者在必要时使其成为一个具体类