使用超类引用调用重载的继承方法

Posted

技术标签:

【中文标题】使用超类引用调用重载的继承方法【英文标题】:Calling overloaded inherited methods using super class reference 【发布时间】:2015-07-04 08:01:39 【问题描述】:

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

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在哪里?

我还检查了我的 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的方法?我正在尝试理解这种行为。

【问题讨论】:

请注意,将 @Override 添加到第二个 m1 将阻止这种情况发生 IRL :) Java overloading and inheritance rules的可能重复 【参考方案1】:

您的两个名为m1 的方法没有相同的签名;超类中的一个采用double,子类中的一个采用int。这意味着编译器将根据变量的编译时类型(C1)选择要调用的方法签名,并将调用m1(double)。由于在运行时 C2 类没有覆盖 m1(double) 的版本,因此会调用来自 C1 的版本。

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

【讨论】:

所以,我必须强制转换吗?或创建C2 c = new C2(); @Rob3 这个问题没有说明C2 中的方法m1 是重载的,而不是overriden。详细解释见我的回答。 @Rob3 你必须做一个或另一个来调用m1(int)。将目标的编译时类型更改为C2 问题标题说重载行为没有覆盖【参考方案2】:

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

如果您想在 C2 中调用 m1,则必须强制转换引用,以便编译器接受您正在执行的操作。

【讨论】:

这就是 OP 所说的。他正在重载方法。 他期望调用 overloaded 方法吗?我觉得他没那么笨 我尝试调用另一种方法,但它没有出现。我还认为 C2 首先有两种方法:m1(double) 和第二种方法:m1(int) 当我调用 m1(10) 并期望m1(int ) 将被调用。 要调用其他方法,你必须强制转换对象,示例代码:((C2)c).m1(10); @Rob3 阅读了 SRobertz 的答案。您可见的方法取决于变量 TYPE 而不是实际对象。【参考方案3】:

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

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

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

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

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

【讨论】:

【参考方案4】:

Java 对静态类型进行方法分派,而您的变量 c 的类型为 C1,因此 m1(int) 不可见,您的 10 被强制转换为 double

【讨论】:

这对我来说似乎是最短和最好的答案。 这个答案是最简单的解释。一般来说,在 C1 对象上调用 m1(int) 是不正确的,即使实例是 C2,因为通常 C1 类型的对象没有这样的方法。【参考方案5】:

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

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 in expand to double] 兼容。所以int提升为double,调用对应的方法(这也是你在字节码中看到的)。

【讨论】:

【参考方案6】:

如果您调用 c.m1(10.0),它将按照您最初的预期调用祖先的方法。

您在示例中进行方法重载(即添加更多具有相同名称和不同签名的方法)而不是方法覆盖(即通过使用相同的签名重新声明祖先方法在后代中的实现来更改它, AKA 相同名称和相同类型的结果和方法参数 - 参数名称应该无关紧要)。

【讨论】:

【参考方案7】:

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

所以如果你在 C1 方法中有 int 类型,那么你需要在 C2 方法中也有 int 类型,然后 JVM 会按照你的意愿从 C2 运行方法。

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

【讨论】:

【参考方案8】:

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

C1 c = new C2();

C2 c = new C2();

【讨论】:

【参考方案9】:

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

注意:现在在C2 类中,您有两个重载方法 - m1(int num)m1(double num)。而C2.m1(int num) 是与C2.m1(double num) 不同的方法。

【讨论】:

【参考方案10】:

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

由于 double 优先于 int,因此调用了具有双参数的方法。这是因为 int 可以分配给 double,但不能反过来。

所以在方法调用(运行)时有很多事情需要考虑。

是的,你的主类应该是这样的

        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

【讨论】:

以上是关于使用超类引用调用重载的继承方法的主要内容,如果未能解决你的问题,请参考以下文章

Java中调用了哪个重载方法

Java继承

继承多态及方法重写重载

Java多态机制和继承中重写重载

技术累积点java23super以及重写重载

覆写(Override)和重载(Overload)的比较