frogatto
frogatto

Reputation: 29285

Why does indexOf method of ArrayList work?

Please consider the following code (Its quite simple)

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));
    }
}

When I run this code, 1 will be logged in the console. In my opinion it shouldn't be 1 rather it should be -1. I've looked into the indexOf source code. Exactly copy/paste from there:

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;
}

As we know when my code wants to be run the else segment of the above code starts to execute. And as you can see in there, statement o.equals(elementData[i]) will be executed. So far everything is okay and I have no problems

My Problem

Type of variable o in the above method is Object and when we call equals method of it, In my opinion equals method of a generic Object will be executed, In the other side I've look at equals method of Object source code. I copy/paste it here:

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

Now, in the for-loop at indexOf method, It shouldn't any item be matched with o object because there is no item in the array which is equal to o object, in == point of view

Now I wonder how is output of my code equals to 1. Can any one please help me?

Thanks

Upvotes: 1

Views: 483

Answers (4)

aioobe
aioobe

Reputation: 421010

The static type of obj is Object but the runtime type is A.

Since all methods in Java are virtual, it doesn't matter what the static type is. It's the runtime type of the callee that will determine which method is called. In this case the runtime type is A thus A.equals is called.

Analogously the snippet below calls String.toString rather than Object.toString because the runtime type of o is String.

Object o = "Hello";
System.out.println(o.toString());

// Prints "Hello" and not something like "java.lang.String@15db9742"

Upvotes: 8

Jon Skeet
Jon Skeet

Reputation: 1500595

You're confusing the compile-time type of a variable with the execution-time type of its value.

The A.equals method will be called rather than Object.equals because of overriding - the execution-time type is used to determine which override of a method is used. If this weren't the case, custom equality overrides could never be used by ArrayList.indexOf. Don't forget that the declared type of obj is entirely unknown to the ArrayList.indexOf code. All it has is the value of the o parameter - which is a reference to an instance of A.

It's really important to differentiate between compile-time types (which are used for overloading, checking what members are available etc) and the values at execution-time (which are used for overriding, instanceof etc).

Upvotes: 6

JochenW
JochenW

Reputation: 1053

Your error is here: "In my opinion equals method of a generic Object will be executed, ". Although the specified type of "o" is "Object", it still has an actual type of "A". Meaning, that o.equals(...) will in fact invoke A.equals, rather than Object.equals.

Upvotes: 1

EpicPandaForce
EpicPandaForce

Reputation: 81539

A extends Object and you have overridden its equals(Object) method. Therefore, via the Object reference, you see its methods that it inherits from Object, but calling it like so will call the equals method you provided.

What you claimed would happen would happen only if your equals(Object) method called super.equals(Object).

If this wasn't the case, heterogenous collections and inheritance would be useless, and OOP would go down the drain.

Upvotes: 0

Related Questions