Java静态和动态绑定,upcast,重载混合在一起

时间:2013-12-01 14:14:24

标签: java inheritance dynamic-binding static-binding

假设我们有以下代码

class TestEqual{
    public boolean equals(TestEqual other ) {
    System.out.println( "In equals from TestEqual" ); return false;
}

    public static void main( String [] args ) {
        Object t1 = new TestEqual(), t2 = new TestEqual();
        TestEqual t3 = new TestEqual();
        Object o1 = new Object();

        int count = 0;
        System.out.println( count++ );// shows 0
        t1.equals( t2 ) ;
        System.out.println( count++ );// shows 1
        t1.equals( t3 );
        System.out.println( count++ );// shows 2
        t3.equals( o1 );
        System.out.println( count++ );// shows 3
        t3.equals(t3);
        System.out.println( count++ );// shows 4
        t3.equals(t2);
    }
}

基本上,在TestEqual类(当然,扩展Object)中,我们有一个方法从Object重载equals方法。

此外,我们还有一些变量:对象t1,t2实例化为TestEqual,TestEqual t3实例化为TestEqual,Object o1实例化为Object。

如果我们运行程序,这将是输出。

0
1
2
3
In equals from TestEqual
4

这个例子似乎比通常的Car c = new Vehicle()更复杂; c.drive();因为我们调用方法的对象实例与其类型不同,并且方法的参数实例与其类型不同。

我想检查一下,当我们调用每个方法时,我们是否正确理解会发生什么,一步一步地进行绑定。

 show 0
 t1.equals(t2)
 show 1

t1被视为TestEqual对象。方法equals被重载,因此绑定是静态的,这意味着我们将t2作为Object传递,因此它将调用从Object超类继承的equals方法,因此它不会显示任何文本。

 show 1
 t1.equals(t3)
 show 2

这看起来有点奇怪。我本来希望显示“来自TestEqual的等于”,因为t3是一个TestEqual对象,所以应该调用来自t1的等号。我在这里的解释是t1是静态绑定并被视为一个Object,因此调用继承自Object类的方法equary,参数TestEqual t3被转换为Object。 但这不意味着先前对t1.equals(t2)的解释是错误的吗?

show 2
t3.equals(o1);
show 3

t3是一个TestEqual对象,参数o1是一个Object,因此调用从Object继承的equals方法,因此不打印任何内容。

show 3
t3.equals(t3)
show 4

t3是一个TestEqual对象,该参数是一个TestEqual对象,因此将调用TestEqual中的重载方法,打印“In Equals from TestEqual”。

show 4
t3.equals(t2)

t3是一个TestEqual对象,由于静态绑定(重载方法),参数是一个Object,所以从Object继承的equal方法被调用,什么都不打印。

2 个答案:

答案 0 :(得分:3)

方法Object.equals(Object obj)将另一个Object实例作为参数。 如果您将TestEqual定义为:

class TestEqual{
    @override
    public boolean equals(Object other ) {
        System.out.println( "In equals from TestEqual" ); return false;
    }
}

它会按预期工作。

答案 1 :(得分:3)

  

这看起来有点奇怪。我原本希望显示“等于来自   TestEqual“,因为t3是一个TestEqual对象,所以从t1开始等于   应该叫。我在这里的解释是t1是静态绑定的   并被视为一个Object,因此该方法等于继承自   调用对象类,参数TestEqual t3被上传到Object。但这不意味着先前对t1.equals(t2)的解释是错误的吗?

要在重载的上下文中调用方法,会发生大多数特定的方法调用,并在编译时确定。选择最具体方法的规则在java language specification 15.12.2.5. Choosing the Most Specific Method中定义:通过其他讨论,提到的陈述是:

  

方法m1严格比另一种方法m2更具体,如果和   只有当m1比m2更具特异性且m2不是更具体时   M1。

为了解释你的上下文,但是让我们声明两个简单的Super类和sup类:

class SuperA
{
    public void test(SuperA a)
    {
        System.out.println("super class's test() is called");
    }
}

class SubB extends SuperA
{

    public void test(SubB subB)
    {
        System.out.println("subclass's test() is called");


    }    
}

现在,如果我们以这种方式创建两个实例:

SuperA obj = new SubB();
SubB obj2 = new SubB();
obj.test(obj2);

您将看到超类的test()被调用,因为它在编译时被确定为更具体,并且编译器看到objSuperA类型的实例。现在将obj投射到SuubB并调用test(obj2)

((SubB)obj).test(obj2); // cast to SubB

并打印:"subclass's test() is called"暗示它调用test(obj)的{​​{1}}方法,因为此时编译器知道SubB的类型为obj且用于调用SubB的最具体的解决方案。

但是,现在让我们以这种方式声明两个实例:

test

在这一系列的调用中,前两个语句将调用超类 SuperA obj = new SubB(); SuperA obj2 = new SubB(); obj.test(obj2); // invokes super class's test method ((SubB)obj).test(obj2);// invokes super class's test method ((SubB)obj).test((SubB)obj2); // invoke sub class's test method 的测试方法,因为这些调用更具体。然而,解释第二种情况:

SuperA

这次编译器知道((SubB)obj).test(obj2);// invokes super class's test method 的类型为obj,但它仍然看到SubB的{​​{1}}类型更具体obj2方法调用。因此,对于第二个示例,其中SperAtest都声明为obj类型:我们需要将它们都强制转换为obj2以调用SuperA 'SubB方法。

由于SubB是java中所有类的超类,所以你现在应该理解,但是你的上下文的test方法调用系统。为了避免这种调用陷阱,您将看到java类中实现的所有Object方法实际上是{em>覆盖 equal方法equal类并且已制作使用equal检查。例如,Object class的等值方法实现:

instanceof