2

假设我们有以下代码

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 方法的方法。

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

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

0
1
2
3
In equals from TestEqual
4

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

我想检查我是否正确理解了当我们调用每个方法时会发生什么,逐步关于绑定。

 show 0
 t1.equals(t2)
 show 1

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

 show 1
 t1.equals(t3)
 show 2

这似乎有点奇怪。我本来希望显示“In equals from TestEqual”,因为 t3 是一个 TestEqual 对象,所以应该调用来自 t1 的 equals。我在这里的解释是 t1 是静态绑定的并且被视为一个对象,因此调用从 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 equals from TestEqual”。

show 4
t3.equals(t2)

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

4

2 回答 2

3

这似乎有点奇怪。我本来希望显示“In equals from TestEqual”,因为 t3 是一个 TestEqual 对象,所以应该调用来自 t1 的 equals。我在这里的解释是 t1 是静态绑定的并且被视为一个对象,因此调用从 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()调用了超类,因为它在编译时被确定为更具体,并且编译器看到它obj是 type 的实例SuperA。现在obj转换 toSuubB并调用test(obj2)

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

并打印:"subclass's test() is called"暗示它调用 的test(obj)方法,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但它仍然看到它obj2的类型SperA更具体地用于test方法调用。因此,对于第二个示例,其中objandobj2都声明为 type SuperA:我们将需要将它们都转换SubB为调用SubB'Stest方法。

作为Objectjava中所有类的超类,你现在应该明白了,但是equal你的上下文的方法调用系统。为了避免这种调用陷阱,你会看到equaljava类中所有实现的方法实际上都是 方法类的重写,并使用了检查。比如类的equal方法实现:equalObjectinstanceofInteger

public boolean equals(Object obj) {
        if (obj instanceof Integer) { //<<<---- instance of checking
            return value == ((Integer)obj).intValue();
        }
        return false;
    } 
于 2013-12-01T15:22:54.020 回答
3

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

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

它将按您的预期工作。

于 2013-12-01T14:18:52.803 回答