繁体   English   中英

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

[英]Java static and dynamic binding, upcast, overloading mixed together

假设我们有以下代码

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和对象o1实例为对象。

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

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类继承的equals方法被调用,参数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 from equals TestEqual”。

show 4
t3.equals(t2)

t3是一个TestEqual对象,由于静态绑定(重载方法),该参数是一个Object,因此调用了从Object继承的equal方法,不打印任何内容。

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

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

它会按您期望的那样工作。

这似乎有点不可思议。 我本来希望显示“来自TestEqual的等于”,因为t3是一个TestEqual对象,所以应该调用来自t1的等于。 我在这里的解释是t1是静态绑定的并且被视为Object,因此称为从Object类继承的equals方法被调用,参数TestEqual t3被向上转换为Object。 但这是否意味着从t1.equals(t2)开始的先前解释是错误的?

要在重载的情况下调用方法,会发生最具体的方法调用,并在编译时确定。 选择最特定方法的规则在Java语言规范15.12.2.5中定义 选择最具体的方法 :在其他讨论中,提到的陈述是:

当且仅当m1比m2更特定且m2不比m1更具体时,方法m1才比另一方法m2严格更具体。

为了解释您的上下文,但是让我们声明两个简单的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类型的实例。 现在将objSuubB并调用test(obj2)

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

并显示: "subclass's test() is called"表示它调用SubBtest(obj)方法,因为这一次编译器知道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的类型,但仍然看到obj2具有SperA的类型,它更特定于test方法调用。 因此,对于第二个示例,其中objobj2都使用SuperA类型SuperA :我们将需要将它们都SubB转换为SubB才能调用SubBtest方法。

因为Object是Java中所有类的超类,所以您现在应该了解,但是上下文的equal方法调用系统。 为了避免这种调用陷阱,您将看到java类中所有实现的equal方法实际上是equal方法Object类的替代 ,并使用了instanceof检查。 例如, Integer类的equal方法实现:

public boolean equals(Object obj) {
        if (obj instanceof Integer) { //<<<---- instance of checking
            return value == ((Integer)obj).intValue();
        }
        return false;
    } 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM