views:

1589

answers:

4

Before I look through my generic data structure for a value's index, I'd like to see if it is even an instance of the type this has been parametrized to. But Eclipse complains when I do this:

@Override
public int indexOf(Object arg0) {
 if (!(arg0 instanceof E)) {
  return -1;
 }

What is the better way to do it?

This is the error message

Cannot perform instanceof check against type parameter E. Use instead its erasure Object since generic type information will be erased at runtime

A: 
arg0 instanceof this.class()

Maybe that would do it.

ONi
No it wouldn't. Firstly, `this.class()` is a compilation error. Secondly, `arg0 instanceof this.getClass()` tests if `arg0` is an instance of the (umm ...) data structure class. The OP wants to test if it is an instance of the (umm ...) element class.
Stephen C
+9  A: 

The error message says it all. At runtime, the type is gone, there is no way to check for it.

You could catch it by making a factory for your object like this:

 public static <T> MyObject<T> createMyObject(Class<T> type) {
    return new MyObject<T>(type);
 }

And then in the object's constructor store that type, so variable so that your method could look like this:

        if (arg0 != null && !(this.type.isAssignableFrom(arg0.getClass()))
        {
            return -1;
        }
Yishai
I don't think you want `Class.isAssignableFrom`.
Tom Hawtin - tackline
@Tom, I wrote this last night from memory, and I fixed it to actually pass the class (duh!) but otherwise, I don't understand why you wouldn't want it (maybe I need more coffee this morning, I'm only on my first cup).
Yishai
@Yishai - I'm with Tom. Could you clarify that? Using isAssignableFrom() would have been my pick for the job. Maybe I'm missing something?
luis.espinal
@luis, Tom's comment probably meant I should use isInstance() and pass the actual arg0 parameter. It has the advantage of avoiding the null check.
Yishai
+1  A: 

Technically you shouldn't have to, that's the point of generics, so you can do compile-type checking:

public int indexOf(E arg0) {
   ...
}

but then the @Override may be a problem if you have a class hierarchy. Otherwise see Yishai's answer.

Jason S
yeah, the List interface demands that the function take an object parameter.
Rosarch
you're implementing List? Why aren't you implementing List<E> ?
Jason S
(see for example the declaration of ArrayList: http://java.sun.com/j2se/1.5.0/docs/api/java/util/ArrayList.html)
Jason S
@Rosarch: the List interface's indexOf() method does not require that the argument given is the same type as the object you are looking for. it just has to be .equals() to it, and objects of different types can be .equals() to each other. This is the same issue as for the remove() method, see: http://stackoverflow.com/questions/104799/why-arent-java-collections-remove-methods-generic
newacct
[[[sheepishly]]] never mind, I thought indexOf() required E as a parameter rather than Object. (why did they do that?!??!)
Jason S
@Jason basically because it would break perfectly valid code that didn't require an potentially unsafe type cast. Also, because Generics are not covariant you can get into some edge cases with type parameters (say a list of lists that itself has a type parameter) that the compiler would block even though the indexOf() would actually return a real index (the object is in the list but the compiler can't let you see it).
Yishai
+1  A: 

The runtime type of the object is a relatively arbitrary condition to filter on. I suggest keeping such muckiness away from your collection. This is simply achieved by having your collection delegate to a filter passed in a construction.

public interface FilterObject {
     boolean isAllowed(Object obj);
}

public class FilterOptimizedList<E> implements List<E> {
     private final FilterObject filter;
     ...
     public FilterOptimizedList(FilterObject filter) {
         if (filter == null) {
             throw NullPointerException();
         }
         this.filter = filter;
     }
     ...
     public int indexOf(Object obj) {
         if (!filter.isAllows(obj)) {
              return -1;
         }
         ...
     }
     ...
}

     final List<String> longStrs = new FilterOptimizedList<String>(
         new FilterObject() { public boolean isAllowed(Object obj) {
             if (obj == null) {
                 return true;
             } else if (obj instanceof String) {
                 String str = (String)str;
                 return str.length() > = 4;
             } else {
                 return false;
             }
         }}
     );
Tom Hawtin - tackline
(Although you might want to do an instance type check if you are using `Comparator` or similar.)
Tom Hawtin - tackline