Java静态和动态绑定,重载

时间:2016-01-18 23:47:18

标签: java

我正在练习测试,我遇到了关于重载和静态和动态绑定的练习。要求输出以下代码:

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

4 个答案:

答案 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());方法,printC(因为没有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的方法版本。

所以是的,永远不要这样做= \