使用 super class 引用调用重载的继承方法

Calling overloaded inherited methods using super class reference

我不理解这种 Java 行为。我有两个 classes:

class C1 {
    public void m1(double num) {
        System.out.println("Inside C1.m1(): " + num);
    }
}

class C2 extends C1 {
    public void m1(int num) {
        System.out.println("Inside C2.m1(): " + num);
    }
}

这是我的主要内容:

public class Main {

    public static void main(String[] args) {
        C1 c = new C2();
        c.m1(10);
    }
}

结果是:

Inside C1.m1(): 10.0

当我预期的时候:

Inside C2.m1(): 10

另外,当我尝试完成代码语法时,我发现了这个:

C2的另一个m1在哪里class?

我还检查了我的 Main.class 的字节码,我看到了这个:

Compiled from "Main.java"
public class com.company.Main {
  public com.company.Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/company/C2
       3: dup
       4: invokespecial #3                  // Method com/company/C2."<init>":()V
       7: astore_1
       8: aload_1
       9: ldc2_w        #4                  // double 10.0d
      12: invokevirtual #6                  // Method com/company/C1.m1:(D)V
      15: return
}

字节码告诉我它将调用 C1.m1 (D)V(第 12 行)。

为什么是C1的方法?我试图理解这种行为。

是参数的原因。您调用的方法是带有双参数的方法。 C2 内部的 m1 并没有覆盖它,而是超载了它。

如果你想在 C2 中调用 m1,你必须转换引用以使编译器接受你正在做的事情。

Java 对静态类型进行方法调度,而你的变量 c 是类型 C1,所以 m1(int) 不可见,而你的 10 转换为 double.

由于C1.m1(double num)是public方法,所以继承了C2。所以你的 C2 也有一个方法,m1(double num),这就是它被调用的原因。从 main() 你实际上调用了 C2.m1(double num).

注意:现在在 class C2 你有两个重载方法 - m1(int num)m1(double num)C2.m1(int num) 是与 C2.m1(double num) 不同的方法。

两种方法的方法签名不同。

public void m1(double num) 
public void m1(int num)

所以在这种情况下没有覆盖。现在当你说

    C1 c = new C2();
    c.m1(10);

在编译时,它将看到类型为 C1 的引用,其方法 public void m1(double num) 与 10 [int 扩展为 double] 兼容。所以int被提升为double,并调用相应的方法(这也是你在字节码中看到的)。

您的两个名为 m1 的方法没有相同的签名; superclass中的取一个double,subclass中的取一个int。这意味着编译器将 select 根据变量的编译时类型调用的方法签名,即 C1,并将调用 m1(double)。由于在运行时 class C2 没有 m1(double) 的覆盖版本,因此调用 C1 的版本。

规则是方法signatures是在编译时根据编译时类型计算的;方法调用在运行时根据匹配的签名调度

通过查看你的代码,你没有利用继承来获得你想要的答案。你必须改变这一行

C1 c = new C2();

C2 c = new C2();

您看不到 C2 的方法,因为您的实例变量被声明为 C1,也因为它们没有相同的签名。您在一个方法中有双参数,在第二个方法中有一个 int 类型,这使得它们对于 JVM 完全不同的方法(因此这里没有继承)。

因此,如果您在 C1 方法中有 int 类型,那么您还需要在 C2 方法中有 int 类型,然后 JVM 将 运行 来自 C2 的方法,就像您想要的那样。

您还可以将变量转换为 C2 类型,然后您将能够访问 C2 的方法。

您看到输出为 Inside C1.m1(): 10.0 而不是 Inside C1.m1(): 10Inside C2.m1(): 10.0 的原因是:

  1. 您没有重写 C2 中的方法 m1。您将从 C1 继承的 m1(doube) 方法重载到 m1(int)
  2. C2 class 现在有两个 m1 方法。一个来自 C1inherited 并且具有签名 m1(double) 和一个在 C2 中重载并且具有签名 m1(int)
  3. 当编译器看到调用 c.m1(10) 时,它会根据引用类型解析此调用。由于引用类型是 C1,编译器将在 C1 中解析对 m1(double) 的调用。
  4. 在运行时,JVM 将在 C2 中解析对 m1(double) 的调用,这是从 C1 继承的方法。 (如第 2 点所述)

m1(int)方法有两种调用方式:

((C2)c).m1(10);

C2 c = new C2(); c.m1(10);

如果你调用 c.m1(10.0),它会像你最初期望的那样调用祖先的方法。

您在您的示例中进行了方法重载(即添加更多具有相同名称和不同签名的方法)而不是方法重写(即通过使用相同的签名,也就是相同的名称和相同类型的结果和方法参数 - 参数名称无关紧要)。

Java 选择最具体的适用类型。在这种情况下,m1(int) 不适用。 强调 class 的引用,该引用包含相同 class(c1) 的对象或 class(c2) 的任何子 classes 的对象以及方法名称和参数列表.

正在调用您的带有双参数的方法,因为双参数优先于 int。这是因为 int 可以赋值给 double,反之则不行。

所以在方法调用的(运行)时间要考虑的事情太多了。

是的,对于你的情况,你的主要 class 应该是这样的

        public static void main(String[] args) {
            C1 c = new C2();
            c.m1(10);
            ((C2) c).m1(10);
    //or
            C2 cobj = new C2();
            cobj.m1(10);
        }


**OutPut**
Inside C1.m1(): 10.0
Inside C2.m1(): 10
Inside C2.m1(): 10