Method的重写(override)与隐藏(hide)

陈 欣发布

Instance Method

child class中的instance method如果和parent class的某一instance method有一致的签名(signature)和返回类型,则会重写parent class的该method。

重写的能力有助于child class继承行为类似的parent class,在此基础上继续按需求定制特定method的行为。重写的method与parent class中的method有一致的名字、参数类型/数目和返回类型。重写的方法也可以返回parent class中method返回类型的child,这称为协变返回类型(covariant return type)

当重写方法时,你可以使用@Override注释来提示编译器该method重写了parent class的method。这样一旦编译器发现parent class中没有对应的method就会报错。

Static Method

如果child class定义了与parent class中某一static method签名一致的static method,该方法则会隐藏parent class的static method。

重写instance method和隐藏static method有重要的区别:

  • 重写的instance method版本始终会被调用。
  • 调用哪个存在隐藏关系的static method版本则取决于是从child class还是parent class调用。

考虑以下这两个class。第一个名为Animal,有一个instance method和一个static method:

public class Animal {
    public static void testClassMethod() {
        System.out.println("The static method in Animal");
    }
    public void testInstanceMethod() {
        System.out.println("The instance method in Animal");
    }
}

第二个类是Animal名为Cat的child class:

public class Cat extends Animal {
    public static void testClassMethod() {
        System.out.println("The static method in Cat");
    }
    public void testInstanceMethod() {
        System.out.println("The instance method in Cat");
    }

    public static void main(String[] args) {
        Cat myCat = new Cat();
        Animal myAnimal = myCat;
        Animal.testClassMethod();
        myAnimal.testInstanceMethod();
    }
}

Cat重写了Animal的instance method并隐藏了static method。main method中创建了Cat的一个object并调用parent class的testClassMethod()及类型转换为parent的object的testInstanceMethod()

程序的输出如下:

The static method in Animal
The instance method in Cat

与我们的预期一致,被隐藏的method当从parent class调用时可以回到之前的版本,而被重写的method则始终是child object的版本。

Interface Method

default method和abstract method与instance method一样继承。然而当interface提供多个签名一致的default method时,Java编译器按照如下的两个原则解决命名冲突。

  • instance method的优先级高于interface中的default method。

考虑下面的class和interface:

public class Horse {
    public String identifyMyself() {
        return "I am a horse";
    }
}
public interface Flyer {
    default public String identifyMyself() {
        return "I am able to fly";
    }
}
public interface Mythical {
    default public String identifyMyself() {
        return "I am a mythical creature";
    }
}
public class Pegasus extends Horse implements Flyer, Mythical {
    public static void main(String... args) {
        Pegasus myApp = new Pegasus();
        System.out.println(myApp.identifyMyself());
    }
}

method Pegasus.identifyMyself将返回I am a horse

  • 已经被重写的method会被忽略。这当多个interface拥有共同祖先时会发生。

考虑下面的interface和class:

public interface Animal {
    default public String identifyMyself() {
        return "I am an animal";
    }
}
public interface EggLayer extends Animal {
    default public String identifyMyself() {
        return "I am able to lay eggs";
    }
}
public interface FireBreather extends Animal { }
public class Dragon implements EggLayer, FireBreather {
    public static void main (String... args) {
        Dragon myApp = new Dragon();
        System.out.println(myApp.identifyMyself());
    }
}

method Dragon.identifyMyself将返回I am able to lay eggs

当两个或以上独立定义的default method冲突,或default method与abstract method冲突时,Java编译器会报错。你必须显式重写所实现的interface中的这些method。

考虑下面电脑控制的飞行汽车示例。两个interface(OperateCarFlyCar)对同一startEngine method提供了默认实现。

public interface OperateCar {
    // ...
    default public int startEngine(EncryptedKey key) {
        // Implementation
    }
}
public interface FlyCar {
    // ...
    default public int startEngine(EncryptedKey key) {
        // Implementation
    }
}

同时实现OperateCarFlyCarclass FlyingCar就必须重写startEngine method。可以使用super关键字调用任一interface中的默认实现。

public class FlyingCar implements OperateCar, FlyCar {
    // ...
    public int startEngine(EncryptedKey key) {
        FlyCar.super.startEngine(key);
        OperateCar.super.startEngine(key);
    }
}

super前的名字(在例子中即为OperateCarFlyCar)必须是所实现的interface之一,这个interface定义或继承了所调用的method。这种对于default method的调用方式并不局限于interface,也适用于class。

从parent class继承而来的instance method会重写interface的abstract method。考虑下面的interface和class:

public interface Mammal {
    String identifyMyself();
}
public class Horse {
    public String identifyMyself() {
        return "I am a horse";
    }
}
public class Mustang extends Horse implements Mammal {
    public static void main(String... args) {
        Mustang myApp = new Mustang();
        System.out.println(myApp.identifyMyself());
    }
}

method Mustang.identifyMyself将返回I am a horse。class Mustang从class Horse继承了method identifyMyself,其重写了interface Mammal中的同名abstract method。

访问权限

重写的method可以比parent class中对应method有更多的访问权限,但不能更少。例如protected method可被重写为public,但不能为private

不能在child class中变更method的static状态,否则在编译时会报错。

总结

下表概括了child class所定义的method与parent class中method签名相同时的各种情况。

parent instance method parent static method
child instance method 重写 编译错误
child static method 编译错误 隐藏

陈 欣

AADPS创始人

1 条评论

单人纸牌(Elevens Lab)活动1:设计并创建class Card – AP Computer Science · 2018年5月12日 下午3:16

[…] 阅读目录里已经部分完成的class Card框架。在读的时候,你会注意到在toString method上使用的@Override注释。Java中,@Override注释被用来表明某个method一定重写了parent class中的同名method。在这个例子中,class Object(Java所有class默认的parent class)的toString method要被class Card的对应method重写。如果该method并没有实际重写parent class的对应method,Java编译器会报错。 […]

发表回复