请考虑以下代码(非常简单)
public class Main {
public static class A{
int id;
public A(final int id){
this.id = id;
}
@Override
public boolean equals(final Object obj) {
if(obj instanceof A){
final A a = (A) obj;
return id == a.id;
}
return false;
}
}
public static void main(final String[] args) {
final List<A> items = new ArrayList<A>();
items.add(new A(0));
items.add(new A(1));
final Object obj = new A(1);
System.out.println(items.indexOf(obj));
}
}
当我运行此代码时,将在控制台中记录1
。在我看来,它不应该是1
,而应该是-1
。我已查看了indexOf
source code。从那里完全复制/粘贴:
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
正如我们所知,当我的代码想要运行时,上述代码的else
段开始执行。正如您在那里看到的,语句o.equals(elementData[i])
将被执行。到目前为止一切都还可以,我没有问题
我的问题
上述方法中变量o
的类型为Object
,当我们调用它的equals
方法时,我认为通用对象的equals
方法将被执行,另一方面,我查看了对象source code的equals
方法。我在这里复制/粘贴它:
public boolean equals(Object obj) {
return (this == obj);
}
现在,在indexOf
方法的for循环中,它不应该与o
对象匹配任何项,因为数组中没有项等于{{1 } {object},o
观点
现在我想知道我的代码输出是如何等于==
的。任何人都可以帮助我吗?
由于
答案 0 :(得分:8)
obj
的静态类型为Object
,但运行时类型为A
。
由于Java中的所有方法都是virtual,因此静态类型并不重要。它是将确定调用哪个方法的被调用者的运行时类型。在这种情况下,运行时类型为A
,因此调用A.equals
。
类似下面的代码段会调用String.toString
而非Object.toString
,因为o
的运行时类型为String
。
Object o = "Hello";
System.out.println(o.toString());
// Prints "Hello" and not something like "java.lang.String@15db9742"
答案 1 :(得分:6)
您将变量的编译时类型与其值的执行时间类型混淆。
由于覆盖,将调用A.equals
方法而不是Object.equals
- 执行时间类型用于确定使用哪种方法覆盖。如果不是这种情况,ArrayList.indexOf
永远不会永远使用自定义相等覆盖。不要忘记obj
代码完全不知道ArrayList.indexOf
的声明类型。它只有o
参数的值 - 它是对A
实例的引用。
区分编译时类型(用于重载,检查可用成员等)和执行时的值(用于覆盖,instanceof
等)非常重要。 / p>
答案 2 :(得分:1)
你的错误在这里:&#34;在我看来,等于通用对象的方法将被执行,&#34;。虽然指定的类型&#34; o&#34;是&#34;对象&#34;,它仍然具有&#34; A&#34;的实际类型。意思是,o.equals(...)实际上会调用A.equals,而不是Object.equals。
答案 3 :(得分:0)
A
扩展Object
,您已覆盖其equals(Object)
方法。因此,通过Object
引用,您会看到它从Object
继承的方法,但是调用它会调用您提供的equals
方法。
只有当您的equals(Object)
方法调用super.equals(Object)
时才会发生您所声称的内容。
如果情况并非如此,那么异类收集和继承将毫无用处,OOP将会耗尽。