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

时间:2015-04-25 18:37:27

标签: java oop inheritance syntax overloading

我不明白这种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

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

Enter image description here

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的方法?我试图理解这种行为。

10 个答案:

答案 0 :(得分:16)

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

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

答案 1 :(得分:10)

这是因为参数。您调用的方法是具有double参数的方法。 C2中的m1不会覆盖它,而是重载它。

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

答案 2 :(得分:7)

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

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

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

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

答案 3 :(得分:6)

Java对静态类型进行方法调度,而变量c的类型为C1,因此m1(int)不可见,并且10被强制转换为{{ 1}}。

答案 4 :(得分:4)

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

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 expanded to double]兼容。因此int被提升为double,并调用相应的方法(这也是你在字节码中看到的)。

答案 5 :(得分:3)

如果你打电话给c.m1(10.0),它会像你原先想象的那样调用祖先的方法。

你在你的例子中做了方法重载(即添加更多具有相同名称和不同签名的方法)而不是方法覆盖(即在后代改变祖先方法的实现)使用相同的签名,AKA相同的名称和相同类型的结果以及方法参数重新声明它 - 参数名称不应该重要。

答案 6 :(得分:2)

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

C1 c = new C2();

C2 c = new C2();

答案 7 :(得分:2)

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

因此,如果你在C1方法中有int类型,那么你需要在C2方法中也有int类型,然后JVM将像你想要的那样从C2运行方法。

此外,您可以将变量转换为C2类型,然后您就可以访问C2的方法。

答案 8 :(得分:1)

由于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)不同的方法。

答案 9 :(得分:0)

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

正在调用带有double参数的方法,因为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