覆盖和隐藏方法

Instance Methods

子类中具有相同签名(名称,加上数字和其参数的类型)且返回类型作为超类中的实例方法的实例方法将覆盖超类的方法。

子类重写方法的能力使类可以从行为“足够接近”的超类继承,然后根据需要修改行为。覆盖方法与它覆盖的方法具有相同的名称,数量和参数类型,以及返回类型。重写方法还可以返回重写方法返回的类型的子类型。此子类型称为协变返回类型。

覆盖方法时,您可能需要使用@Override注解来指示编译器您打算覆盖超类中的方法。如果由于某种原因,编译器检测到该方法在超类之一中不存在,则它将生成错误。有关@Override的更多信息,请参见Annotations

Static Methods

如果子类定义的静态方法具有与超类中的静态方法相同的签名,则子类中的方法会将其隐藏在超类中。

隐藏静态方法和覆盖实例方法之间的区别具有重要意义:

  • 被调用的覆盖实例方法的版本是子类中的版本。

  • 调用的隐藏静态方法的版本取决于是从超类还是从子类调用。

考虑一个包含两个类的示例。第一个是Animal,其中包含一个实例方法和一个静态方法:

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

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中的实例方法,并隐藏Animal中的静态方法。此类中的main方法创建Cat的实例,并在该类上调用testClassMethod()并在该实例上调用testInstanceMethod()

该程序的输出如下:

The static method in Animal
The instance method in Cat

如所承诺的,被调用的隐藏静态方法的版本是超类中的版本,而被调用的重写实例方法的版本是子类中的版本。

Interface Methods

interface中的Default methodsabstract methods像实例方法一样被继承。但是,当类或interface的超类型提供具有相同签名的多个默认方法时,Java 编译器将遵循继承规则来解决名称冲突。这些规则由以下两个原则驱动:

  • 实例方法优于interface默认方法。

考虑以下类和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());
    }
}

方法Pegasus.identifyMyself返回字符串I am a horse.

  • 已经被其他候选项覆盖的方法将被忽略。当超类型共享一个共同祖先时,就会出现这种情况。

考虑以下interface和类:

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());
    }
}

方法Dragon.identifyMyself返回字符串I am able to lay eggs.

如果两个或多个独立定义的默认方法发生冲突,或者默认方法与抽象方法发生冲突,则 Java 编译器将产生编译器错误。您必须显式重写超类型方法。

考虑一下现在可以飞行的计算机控制汽车的示例。您有两个interface(OperateCarFlyCar),它们为同一方法(startEngine)提供默认实现:

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

同时实现OperateCarFlyCar的类必须覆盖方法startEngine。您可以使用super关键字调用任何默认实现。

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

super之前的名称(在本示例中为FlyCarOperateCar)必须引用直接超interface,该interface定义或继承了所调用方法的默认值。这种形式的方法调用不限于区分包含相同签名的默认方法的多个已实现interface。您可以使用super关键字在类和interface中调用默认方法。

从类继承的实例方法可以覆盖抽象interface方法。考虑以下interface和类:

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());
    }
}

方法Mustang.identifyMyself返回字符串I am a horse.。类Mustang从类Horse继承了方法identifyMyself,该类覆盖了Mammalinterface中同名的抽象方法。

注意 :interface中的静态方法永远不会被继承。

Modifiers

覆盖方法的访问说明符可以比覆盖方法允许更多但不能少于访问。例如,超类中的受保护实例方法可以在子类中公开,但不能私有。

如果您try将超类中的实例方法更改为子类中的静态方法,并且反之亦然,则会出现编译时错误。

Summary

下表总结了在定义具有与超类中的方法相同的签名的方法时发生的情况。

定义签名与超类方法相同的方法

超类实例方法超类静态方法
子类实例方法Overrides产生编译时错误
子类静态方法产生编译时错误Hides

Note:

在子类中,您可以重载从超类继承的方法。这样的重载方法既不会隐藏也不会覆盖超类实例方法,它们是子类独有的新方法。