7

The lastIndexOf() method of List interface accepts a parameter which is of type Object.

However, the add() method accepts a parameter of type E (which is the generic-type of the List which is defined at the time of creating a List) Since add() accepts only E, this prevents the developer (or user) to add any incompatible object to the list at compile-time itself.

Now, Java doc says that lastIndexOf() can throw ClassCastException if the object passed is incompatible. However, when I run the following code in Eclipse Helios I don't get any Exception :-

package scjp.collection.list;

import java.util.ArrayList;
import java.util.List;

public class LastIndexOf {
public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    
    list.add("some");
    list.add("thing");
    list.add("at");
    list.add("last");
    list.add("and");
    list.add("at");
    list.add("again");
    
    
    
    System.out.println(list.lastIndexOf("at"));
    System.out.println(list.lastIndexOf(10));                    // # 1
    System.out.println(list.lastIndexOf(new LastIndexOf()));     // # 2
}
}

At Line 1 and Line 2, I have passed incompatible objects to my List which is of type String. However, the output that I get is :-

5
-1
-1

I get no ClassCastException.

Had the lastIndexOf() method been accepting objects of type E rather than objects of type Object, this would have been prevented at compile-time only. Why is this not done??

Java creators must have thought some problem that could occur if it accept E (and not Object). What harm would it be??

Community
  • 1
  • 1
whitehat
  • 2,381
  • 8
  • 34
  • 47
  • 1
    look at http://docs.oracle.com/javase/1.5.0/docs/api/index.html. It's clearly mentioned that the argument is an Object and now exception is trown. – xyz Jan 31 '12 at 07:44

6 Answers6

2

You don't need the E type -- remember that generics are just type-checked sugar for casts, and equals is defined on Object, so you don't need to cast the item to find out if it's equal to other items in the list. Also, consider bounded wildcard usage. If your list were defined as List<? extends Foo>, you wouldn't be able to get the lastIndexOf for any item other than null.

yshavit
  • 42,327
  • 7
  • 87
  • 124
2

For the same reasons that Collection.remove(Object o) has a similar signature. See this question for more details.

Community
  • 1
  • 1
ChrisH
  • 4,788
  • 26
  • 35
2

In the methods, like remove, it will traverse the Array[E] and try equals() method on each object, then remove by the index if found. The lastIndexOf is the same as below.

public int lastIndexOf(Object o) {
    if (o == null) {
        for (int i = size-1; i >= 0; i--)
        if (elementData[i]==null)
            return i;
    } else {
        for (int i = size-1; i >= 0; i--)
        **if (o.equals(elementData[i]))**
            return i;
    }
    return -1;
}

So if the LastIndexOf class define its own equals function, and you make it equals to string "at", then according to the java spec the lastIndexOf(new LastIndexOf()) should be return the value as lastIndexOf("at").

Also check it out here. Why aren't Java Collections remove methods generic? Another one What are the reasons why Map.get(Object key) is not (fully) generic

Community
  • 1
  • 1
Kleenestar
  • 769
  • 4
  • 4
1

Here's an example that might help. Note that the equals method used by ArrayList<T> will return true if passed a LinkedList<T> with equal elements in the same order. So, if you have a List<ArrayList<Integer>>, it would be valid to call lastIndexOf on it, passing a LinkedList<Integer>, in order to find an equal ArrayList<Integer>. But if lastIndexOf were defined in the way you describe, this wouldn't be possible.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
0

lastIndexOf only cares about the equals() method. So it is happy taking anything, and will only blow up if the underlying equals() method does.

A more type safe way to do it would have been to define an interface "can be checked for equality with", which would work a bit like Comparable. But that's way to much work and not backward compatible.

PaulMurrayCbr
  • 1,167
  • 12
  • 16
-1

One possible way to think of it, is because of wildcards in Generics.

Imagine if you had,

List<? extends Serializable> list = new ArrayList<> extends Serializable>();

(Assuming that all the strings are in the list). If the List API had List.lastIndexOf(E e), then literally, with our example above, this would make it List.lastIndexOf(? extends Serializable) and you couldn't really pass an object other than null.

This is the reason why they put Object as parameter type. One should just implement the object's equals() method correctly for lastIndexOf() search.

Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
  • "this would make it List.lastIndexOf(? extends Serializable) and you couldn't really pass an object other than null." And this is bad because? If it were the case that only objects of the same class could be equal, then the restriction you mention would indeed be necessary to enforce type safety -- if we don't know what class the elements of the list are, then we can't safely search for an element because that element might not be the correct type – newacct Jan 31 '12 at 10:29
  • @newacct, It is bad, because the compiler doesn't know if it's safe to add `String` or `Integer` without java doing [wildcard capture](http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf). Java doesn't know whether the Object is supertype of what you provide. Thus, the compiler might not accept the argument since it's not type-safe. Hence, we need to allow any type (`Object`) and do an equality comparison. – Buhake Sindi Jan 31 '12 at 11:15
  • I don't understand your reply. I am saying that the *only* reason why the argument is type `Object` is because objects of different classes can be equal. If hypothetically Java were made in such a way that a class's equals method could only take an object of the same type, then `lastIndexOf()` should indeed take type `E` for the same reason that `add()` takes type `E` -- it would not be safe otherwise. – newacct Jan 31 '12 at 23:08
  • I understood your statement, I used the approach of why 1) It would be syntactically (compilation wise) to use Generics due to type safety constraints with wildcards and also `equals()` wouldn't have worked in terms of Generics wildcard. – Buhake Sindi Feb 01 '12 at 07:56