关于Java重载的问题&动态绑定

时间:2011-04-14 04:29:57

标签: java overloading dynamic-binding

在下面的代码中,第一个和第二个打印语句如何打印出SubObj? top和sub是否指向同一个Sub类?

class Top {
    public String f(Object o) {return "Top";}
}

class Sub extends Top {
    public String f(String s) {return "Sub";}
    public String f(Object o) {return "SubObj";}
}

public class Test {
    public static void main(String[] args) {  
        Sub sub = new Sub();
        Top top = sub;
        String str = "Something";
        Object obj = str;


        System.out.println(top.f(obj));
        System.out.println(top.f(str));
        System.out.println(sub.f(obj));
        System.out.println(sub.f(str));
    }
}

以上代码返回结果。

SubObj
SubObj
SubObj
Sub

6 个答案:

答案 0 :(得分:11)

由于你已经理解了案例1,3和4,让我们解决案例2。

(请注意 - 我绝不是JVM或编译器内部工作方面的专家,但这是我理解它的方式。如果有人读这篇文章是JVM专家,请随时编辑任何差异的答案你可能会发现。)

子类中具有相同名称但签名不同的方法称为方法重载。方法重载使用静态绑定,这基本上意味着在编译时将强制“选择”(即绑定)适当的方法。编译器不知道对象的运行时类型(也就是实际类型)。所以当你写:

                         // Reference Type  // Actual Type
    Sub sub = new Sub(); // Sub                Sub
    Top top = sub;       // Top                Sub

编译器只“知道”top是Top类型(也就是引用类型)。所以当你后来写道:

    System.out.println(top.f(str)); // Prints "subobj"

编译器“看到”调用'top.f'作为引用Top类的f方法。它“知道”str是String类型,它扩展了Object。因此,1)调用'top.f'引用Top类的f方法,2)类Top中没有f方法接受String参数,3)因为str是Object的子类,Top类的f方法是编译时唯一有效的选择。因此编译器隐式地将str向上转换为其父类型Object,因此可以将其传递给Top的f方法。 (这与动态绑定形成对比,其中上述代码行的类型解析将推迟到运行时,由JVM而不是编译器解析。)

然后在运行时,在上面的代码行中,top被JVM向下传播到它的实际类型sub。但是,参数str已由编译器向上转换为Object类型。所以现在JVM必须在类sub中调用f方法,该方法接受Object类型的参数。

因此,上面的代码行打印“subobj”而不是“sub”。

有关另一个非常相似的示例,请参阅:Java dynamic binding and method overriding

更新:找到关于JVM内部工作原理的详细文章:

http://www.artima.com/underthehood/invocationP.html

我评论了你的代码,以便更清楚地了解发生了什么:

class Top {
    public String f(Object o) {return "Top";}
}

class Sub extends Top {
    public String f(String s) {return "Sub";} // Overloading = No dynamic binding
    public String f(Object o) {return "SubObj";} // Overriding = Dynamic binding
}

public class Test {
    public static void main(String[] args) {  

                                  // Reference Type     Actual Type
        Sub sub = new Sub();      // Sub                Sub
        Top top = sub;            // Top                Sub
        String str = "Something"; // String             String
        Object obj = str;         // Object             String

                                        // At Compile-Time:      At Run-Time:
        // Dynamic Binding
        System.out.println(top.f(obj)); // Top.f (Object)   -->  Sub.f (Object)

        // Dynamic Binding
        System.out.println(top.f(str)); // Top.f (Object)   -->  Sub.f (Object)

        // Static Binding
        System.out.println(sub.f(obj)); // Sub.f (Object)        Sub.f (Object)

        // Static Binding
        System.out.println(sub.f(str)); // Sub.f (String)        Sub.f (String)
    }
}

答案 1 :(得分:6)

这是因为 Java中的所有方法调用都是virtual (默认情况下)。

也就是说,分辨率从实际对象(而不是表达式)开始,并“继续”继承链(每个实际对象)输入)直到找到第一个匹配方法。非虚方法将从表达式开始。 (将方法标记为final会使其成为非虚拟方式。)

但是, 确切的方法签名是在编译时确定的 (Java不支持多分派,单一分派仅在运行时根据接收器对象) - 这解释了为什么Sub.f(String)导致“Sub”,例如当Top.f(String)“绑定”到匹配Top.f(Object)的方法时,即使在Top类型的子类型上调用。 (这是在编译时确定的最佳合格签名)。虚拟调度本身也是一样。

快乐的编码。

答案 2 :(得分:2)

这与对象的表观类型有关。在编译时,Java根据您声明对象的类型而不是您实例化的特定类型进行类型检查。

你有一个类型Top的方法f(对象)。所以当你说:

 System.out.println(top.f(obj));

Java编译器只关心对象top是Top类型,唯一可用的方法是将Object作为参数。在运行时,它然后调用实际实例化对象的f(Object)方法。

下一个电话的解释方式相同。

接下来的两个调用被解释为您所期望的。

答案 3 :(得分:1)

是的,他们都指向Sub级。 问题是top只知道

f(Object o)

它只能调用该签名。

sub知道两个签名,必须按参数类型选择。

答案 4 :(得分:1)

在继承中,基类对象可以引用派生类的实例。

这就是Top top = sub;运作良好的方式。

  1. System.out.println(top.f(obj));

    top对象尝试使用f()类的Sub方法。现在f()类中有两个Sub方法,对传递的参数进行类型检查。由于类型为Object,因此会调用f()类的第二个Sub方法。

  2. System.out.println(top.f(str));

    您可以将其解释为(1),即类型为String,以便调用第一个f()函数。

  3. System.out.println(sub.f(obj));

    这很简单,因为您正在调用Sub类本身的方法。既然Sub类中有两个重载方法,这里也会对传递的参数进行类型检查。由于传递的参数是Object类型,因此会调用第二个f()方法。

  4. System.out.println(sub.f(str));

    与3.类似,此处传递的类型为String,因此调用f()类的第一个Sub函数。

  5. 希望这有帮助。

答案 5 :(得分:0)

in

Sub sub = new Sub();
Top top = sub;

你创建了一个sub的实例,然后将它上传到顶部,这使得它只知道顶部存在的方法。 top中存在的方法是public String f(Object o) {return "Top";}

现在该方法也被sub重载,因此当你创建sub的实例并将它向上转发时它将被调用。

另一种表达方式是

子类型作为表观类型,但顶部作为实际类型,因为您将子指定为顶部。 如果它重载实际类型,你将调用表观类型的方法,但你不能调用实际类型中不存在的任何方法