覆盖和重载的方法

Overridden and Overloaded Methods

"Casting affects the selection of overloaded methods at compile time but not overridden methods"是什么意思?

我在 "Overridden methods and dynamic binding" (https://www.oreilly.com/library/view/learning-java-4th/9781449372477/ch06s01.html) 上阅读了以下段落,但我无法理解最后一段

"在上一节中,我们提到重载方法是在编译时由编译器选择的。另一方面,重载方法是在运行时动态选择的。即使我们创建一个子实例class 我们的代码以前从未见过(可能是通过网络加载的新 class),它包含的任何重写方法都在运行时找到并使用,替换了我们上次编译代码时存在的方法。

相比之下,如果我们创建一个新的 class 来实现一个额外的、更具体的重载方法,并用它替换 class 路径中已编译的 class,我们的代码将继续使用它最初发现的实现。这种情况会一直存在,直到我们重新编译我们的代码和新的 class。这样做的另一个影响是强制转换(即明确告诉编译器将对象视为其可分配类型之一)会影响编译时重载方法的选择,但不会影响重写方法。"

我无法理解 "Casting" 行:"Another effect of this is that casting (i.e., explicitly telling the compiler to treat an object as one of its assignable types) affects the selection of overloaded methods at compile time but not overridden methods."

Casting affects the selection of overloaded methods at compile time but not overridden methods

重载的方法在编译时是可见的。但是重写的方法在运行时变得可见。

拇指法则:

Java 根据引用变量的内容而不是引用变量的类型调用覆盖的方法。

下面的例子是不言自明的。希望对你有帮助。

class Animal {
    public void speak() {
        System.out.print("Animal sounds/roars.");
    }
}

class Human extends Animal {
    @Override                     // Method is overridden
    public void speak() {
        System.out.print("Humans talking english.");
    }

    public void speak(String words) {         // Method is overloaded.
        System.out.print("We have brain. We are intelligent."+words);
    }
}

class Earth {
    public static void main(String a[]) {
        Animal a = new Animal();
        a.speak(); // Prints Animal sounds/roars.

        Human h = new Human();
        h.speak();    // Prints "Humans talking english."

        Animal a = h; // Cast to superclass reference variable. However, underlying object is of Human.
        a.speak();    // Prints "Humans talking english." because speak() is known by Animal at compile time. During runtime, 
                      // the object contains the human object and hence java calls human overridden method.

        a.speak("I want to be human."); // Compile time error as speak(..) is not known by Animal at compile time.

    }
}

该行指的是

  • overloaded versions of a method are chosen at compile time, based on the compile-time types of the arguments that you are passing; whereas
  • overridden methods are chosen at run time, based on the classes of the objects on which you call each method.

要理解这种区别,请考虑同时具有覆盖和重载的情况,如下所示。

public class Person {
}
---------------------------------------------------------
public class Postman extends Person {
}
---------------------------------------------------------
public class Dog {
    public void barkAt(Person p) {
        System.out.println("Woof woof");
    }

    public void barkAt(Postman p) {
        System.out.println("Grrrr");
    }
}
---------------------------------------------------------
public class Rottweiler extends Dog {
    @Override
    public void barkAt(Person p) {
        System.out.println("I'm going to eat you.");
    }

    @Override
    public void barkAt(Postman p) {
        System.out.println("I'm going to rip you apart.");
    }
}

在这种情况下,我们调用这些 barkAt 方法之一,就像这样。

Dog cujo = new Rottweiler();
Person pat = new Postman();
cujo.barkAt(pat);

现在在这种特殊情况下,是编译器选择 cujo.barkAt(pat); 是调用 public void barkAt(Person p) 还是 public void barkAt(Postman p) 之类的方法。这些方法是彼此的重载。

为此,编译器会查看传递给方法的表达式的类型 - 即变量 pat。变量 pat 的类型是 Person,因此编译器选择方法 public void barkAt(Person p).

编译器不会做的是选择它是来自Rottweilerclass还是Dog[=60的方法=] 被调用。这发生在 运行 时间,基于调用该方法的对象的 class,而不是 type 你调用方法的变量。

所以在这种情况下,重要的是名为 cujo 的对象的 class。在这个例子中,cujo 是一个 Rottweiler,所以我们得到了该方法的重写版本 - 在 Rottweiler class.

中定义的那个

此示例将打印出 I'm going to eat you

To summarise:

  • The overload is chosen at compile time based on the parameter type.
  • The override is chosen at run time based on the object class.

现在,可以使用强制转换来更改编译器对重载的选择。 不可能 可以使用强制转换来更改覆盖的 运行 时间选择。所以,我们可以写

cujo.barkAt((Postman) pat);

这一次,传递给方法的参数是Postman类型的表达式。编译器相应地选择重载,这将打印 I'm going to rip you apart..