假设我们有以下代码
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方法,不打印任何内容。
最佳答案
这似乎有点不可思议。我本来希望显示“等于
TestEqual”,因为t3是一个TestEqual对象,所以从t1开始等于
应该叫。我的解释是t1是静态绑定的
并视为对象,因此该方法等于从
调用对象类,将参数TestEqual t3上载到对象。但这是否意味着从t1.equals(t2)开始的先前解释是错误的?
为了在重载的情况下调用方法,会发生最具体的方法调用,并在编译时确定。选择最特定方法的规则在java language specification 15.12.2.5. Choosing the Most Specific Method中定义:在其他讨论中,提到的语句是:
如果和,则方法m1严格比另一个方法m2更具体。
仅当m1比m2更具体且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()
被调用,因为它在编译时被确定为更具体,并且编译器看到obj
是类型为SuperA
的实例。现在将obj
强制转换为SuubB
并调用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
方法调用。因此,对于第二个示例,其中obj
和obj2
都用SuperA
类型声明:我们将需要将它们都强制转换为SubB
来调用SubB
的test
方法。由于
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;
}