【问题标题】:Why does indexOf method of ArrayList work?为什么 ArrayList 的 indexOf 方法有效?
【发布时间】:2014-08-15 08:42:20
【问题描述】:

请考虑以下代码(非常简单)

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。我调查了indexOfsource 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 codeequals 方法。我在这里复制/粘贴它:

public boolean equals(Object obj) {
    return (this == obj);
}

现在,在 indexOf 方法的 for 循环中,不应有任何项目与 o 对象匹配,因为数组中没有等于 o 对象的项目,在 ==观点

现在我想知道我的代码输出如何等于1。谁能帮帮我?

谢谢

【问题讨论】:

    标签: java arraylist indexof


    【解决方案1】:

    objstatic 类型是 Object,但 runtime 类型是 A

    由于 Java 中的所有方法都是virtual,因此静态类型是什么并不重要。被调用者的运行时类型将决定调用哪个方法。在这种情况下,运行时类型是A,因此会调用A.equals

    类似地,下面的 sn-p 调用 String.toString 而不是 Object.toString,因为 o 的运行时类型是 String

    Object o = "Hello";
    System.out.println(o.toString());
    
    // Prints "Hello" and not something like "java.lang.String@15db9742"
    

    【讨论】:

    • 非常感谢您的回答,对我很有帮助,再次感谢
    【解决方案2】:

    您将变量的 compile-time 类型与其值的 execution-time 类型混淆了。

    A.equals 方法将被调用,而不是 Object.equals 因为覆盖 - execution-time 类型用于确定使用哪个方法的覆盖。如果不是这种情况,ArrayList.indexOf 可能永远使用自定义相等覆盖。不要忘记obj 的声明类型对于ArrayList.indexOf 代码是完全未知的。它只有o 参数的 - 这是对A 实例的引用。

    区分编译时类型(用于重载、检查可用成员等)和执行时值(用于覆盖,instanceof 等)非常重要。

    【讨论】:

    • 我不认为混淆是由于 obj instanceof A 评估为真(即使 objObject)而是 A.equals 被调用(而不是 Object.equals ) 因为o 具有静态类型Object。 (为什么 OP 会提出Object.equals 的实现?)
    • 谢谢乔恩,你的回答对我很有帮助
    【解决方案3】:

    您的错误在这里:“在我看来,通用对象的 equals 方法将被执行,”。尽管“o”的指定类型是“Object”,但它的实际类型仍然是“A”。意思是,o.equals(...) 实际上会调用 A.equals,而不是 Object.equals。

    【讨论】:

      【解决方案4】:

      A 扩展 Object 并且您已经覆盖了它的 equals(Object) 方法。因此,通过Object 引用,您可以看到它继承自Object 的方法,但像这样调用它会调用您提供的equals 方法。

      只有当您的 equals(Object) 方法称为 super.equals(Object) 时,您声称会发生的事情才会发生。

      如果不是这样,异构集合和继承将毫无用处,OOP 将付诸东流。

      【讨论】:

        猜你喜欢
        • 2010-11-24
        • 2011-08-17
        • 2017-11-16
        • 1970-01-01
        • 1970-01-01
        • 2022-01-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多