我正在练习测试,我遇到了关于重载和静态和动态绑定的练习。要求输出以下代码:
class Moe {
public void print(Moe p) {
System.out.println("Moe 1");
}
}
class Larry extends Moe {
public void print(Moe p) {
System.out.println("Larry 1");
}
public void print(Larry l) {
System.out.println("Larry 2");
}
}
class Curly extends Larry {
public void print(Moe p) {
System.out.println("Curly 1");
}
public void print(Larry l) {
System.out.println("Curly 2");
}
public void print(Curly b) {
System.out.println("Curly 3");
}
}
class Overloading {
public static void main (String [] args) {
Larry stooge1 = new Curly();
Moe stooge2 = new Larry();
Moe stooge3 = new Curly();
Curly stooge4 = new Curly();
Larry stooge5 = new Larry();
stooge1.print(new Moe());
stooge1.print(new Curly());
stooge1.print(new Larry());
stooge2.print(new Curly());
stooge3.print(new Curly());
stooge3.print(new Larry());
stooge5.print(new Curly());
}
}
我认为我得到第一个但是在其他人身上我完全迷失了。这就是我解决第一个问题的方法:
在运行时stooge1
的类型是Curly
,所以我们调用了Curly的print方法。因为我们传递类型为Moe
的对象进行打印,所以在Moe
中运行参数类型为Curly
的相应打印方法。此方法的输出为Curly 1
,正确答案。
然而,当我将这种技术应用于以下几行时,我得到了错误的答案。有人能解释一下这个概念在Java中是如何运作的吗?
代码的正确输出是:
Curly 1
Curly 2
Curly 2
Larry 1
Curly 1
Curly 1
Larry 2
答案 0 :(得分:4)
静态绑定在编译时发生,在运行时发生动态绑定。
静态绑定负责选择应执行的方法的签名(名称和参数类型)。它使用
编译器从调用了哪种方法的变量类型中选择签名 ,因此Object o = "abc";
将不允许您调用{{ 1}}因为编译器无法在o.substring(1,2);
类中找到substring(int, int)
签名(这是调用了Object
方法的o
变量的类型)。
动态绑定负责在编译时查找并调用静态绑定所选方法的代码 。它将尝试在变量所持有的实例类型中查找方法代码。换句话说,如果你有substring
,你可以得到结果Animal a = new Cat(); a.makeSound();
,因为在运行时JVM将从"Mew"
类开始搜索和调用makeSound
的代码。如果在该类中不提供实现,JVM将在祖先中搜索它,直到找到继承它的那个。
我在你的例子中重新命名了一些类和变量,希望它更具可读性:
Cat
(变量命名 - >类型为class A {
public void print(A a) {
System.out.println("A.print(A)");
}
}
class B extends A {
public void print(A a) {
System.out.println("B.print(A)");
}
public void print(B b) {
System.out.println("B.print(B)");
}
}
class C extends B {
public void print(A a) {
System.out.println("C.print(A)");
}
public void print(B b) {
System.out.println("C.print(B)");
}
public void print(C c) {
System.out.println("C.print(C)");
}
}
class OverloadingDemo {
public static void main (String [] args) {
A ab = new B();
A ac = new C();
B bb = new B();
B bc = new C();
bc.print(new A());
bc.print(new C());
bc.print(new B());
ab.print(new C());
ac.print(new C());
ac.print(new B());
bb.print(new C());
}
}
的{{1}}类型的变量名为X
}。
所以,当我们执行
时Y
xy
中找到可以处理bc.print(new A());
类型实例的最佳print
方法签名。在这种情况下,它将是B
。 A
中搜索此方法的代码(因为这是print(A)
变量持有的实例类型),这意味着我们将看到{ {1}}。同样适用于C
bc
类中的C.print(A)
类找到最佳bc.print(new C());
方法,print
为C
(因为没有B
那里和B是最接近的超类型)。 C
类中查找哪个方法(因为这是print(B)
持有的实例)。因此它会调用print(C)
。
答案 1 :(得分:1)
以下是发生的事情:
stooge1.print(new Moe()); // All three have overload for Moe,
// so the overload from the dynamic type of stooge1 gets called
stooge1.print(new Curly()); // Compiler thinks stooge1 is Larry,
// so it does not know that it has an overload for Curly.
// It uses the overload for Larry instead, because Curly is a Larry
stooge1.print(new Larry()); // Same logic as above applies.
stooge2.print(new Curly()); // Compiler thinks stooge2 is Moe, so its only overload
// is for Moe. Since the dynamic type is Larry, first overload is invoked
其余三种情况可以通过应用与上述相同的逻辑来解决。
答案 2 :(得分:0)
对于#1,#2,#3 stooge1
被声明为Larry
,因此只能调用Larry
可用的方法。
传递Moe
会致电print(Moe)
。由于实际的类是Curly
,它会打印“Curly 1”。
传递Larry
会调用print(Larry)
,因为这是print(Moe)
更好的匹配。这将打印“Curly 2”。
传递Curly
也会调用print(Larry)
。请注意print(Curly)
未知stooge1
,因此编译器无法选择它。因此它还会打印“Curly 2”。
现在试着弄清楚其余部分。
答案 3 :(得分:-1)
所以这是一个你应该永远做不到的令人困惑和可怕的例子。声明的变量类型对于签名方法有什么影响。因此Larry
没有接受Curly
的方法,因此编译器将参数视为Larry
。但它会被发送到Curly
的方法版本。
所以是的,永远不要这样做= \