使用 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(): 10
或 Inside C2.m1(): 10.0
的原因是:
- 您没有重写
C2
中的方法 m1
。您将从 C1
继承的 m1(doube)
方法重载到 m1(int)
。
C2
class 现在有两个 m1
方法。一个来自 C1
的 inherited
并且具有签名 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);
如果你调用 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
我不理解这种 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(): 10
或 Inside C2.m1(): 10.0
的原因是:
- 您没有重写
C2
中的方法m1
。您将从C1
继承的m1(doube)
方法重载到m1(int)
。 C2
class 现在有两个m1
方法。一个来自C1
的inherited
并且具有签名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);
如果你调用 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