使用超类引用调用重载的继承方法
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(): 10
或Inside C2.m1(): 10.0
的原因是:
-
您没有覆盖
C2
中的方法m1
。您正在重载从 C1
继承的 m1(doube)
方法到 m1(int)
。
C2
类现在有两个 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);
【讨论】:
【参考方案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
【讨论】:
以上是关于使用超类引用调用重载的继承方法的主要内容,如果未能解决你的问题,请参考以下文章