views:

189

answers:

5

Given the following code:

public void example(Object o) {
  if(o instanceof List<MyType>)
    //do something
}

I understand that this is not possible (and why its not possible) given the way Java handles generics and type erasure.

My question is, what is the best/cleanest way to accomplish this? Or is the only thing I can do is check if o is a List<?>?

A: 

check the type of the first item?

Segfault
yea, I thought of this...but I'd like to know the type of all elements..maybe the only way would be to iterate through them all..?
kim
A: 

Perhaps by checking the object type at runtime, maybe something like:

if (o.getClass() == List.class) ...

Obviously, you'll have to dig deeper into the object's class type to see if it's an exact match to List<> and the list's element types.

Loadmaster
At runtime, the class of List<ClassA> is exactly the same as List<ClassB>
Brandon Bodnár
Seeing as `List` is an interface, that's probably a bad idea
Adam Batkin
A: 

Short of checking the types of all the items in the List, it is not possible to determine the type of the generic parameter in Java, as this only exists at compile time and is removed prior to runtime.

My suggestion would be unless you absolutely need to have the method take in an Object (such as conforming to a interface spec or overriding Object.equals) to take in the correct type you want as a parameter to the method and to overload the method with the various other types you might need to run the method with.

Brandon Bodnár
+1  A: 

You cannot check more than o instanceof List<?>.

The type safety you get from Java generics happens only at compile-time. A method like yours (which accepts an Object and then tries to figure out what to do), does not play well with this design. I can see why such a dynamic method is necessary sometime, but consider supplementing it with versions for typed parameters:

public void example(Object o) {
  if(o instanceof List<?>)
     example((List)o);    // can only hope that the type is correct here
}

public void example(List<MyType> list){
    // do something
}

In the cases where those can be used, you get the full benefit of generics. In other cases, you have to depend on people reading your Javadoc and only passing in the correct types.

What even the approach above cannot do is have two different code paths for List<TypeA> and List<TypeB>. If this becomes really necessary for you, consider using your own wrapper types ListOfTypeA, ListOfTypeB.

Depending on what you need to do, it may not even be necessary to look at the erased type of the list as a whole, and just work on the runtime types of the individual elements:

 for (Object o: list){
    if (o instanceof TypeA){
    }
    if (o instanceof TypeB){
    }
 }
Thilo
A: 

Java erases type information for generics after compilation so you can't check the type parameter in this way dynamically.

If all paths to this code restrict the type parameter then doing:

// Return true if object is a list of MyType, false if it is not a list, or is 
// empty
boolean isListOfMyType(Object o) {
    if (o instanceof List) {
        List<?> l = (List<?) o;
        return (l.size() > 0 && (l.get(0) instanceof MyType) 
    }
    return true;
}

is typesafe, although it will only work if the list is not empty. If not you will need to modify the above to check whether all the items pass the instanceof test (or if they are null if you allow nulls in your list).

Another alternative is to create a subclass that extends say ArrayList<MyType> and use this for your instanceof check.

Last but not least having a subclass that implements List<MyType> will allow you to get at the type parameter using the Class.getGenericInterfaces() method. See this for details.

For either of these last two methods to work you have to ensure that creation of the list always instantiates one of those types. Ie. if the caller goes and constructs their own ArrayList<MyType> it will not work.

Dean Povey