Java generics and type erasure
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 t开发者_JAVA技巧o accomplish this? Or is the only thing I can do is check if o
is a List<?>
?
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){
}
}
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.
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.
Contrary to what is widely accepted and rarely known type erasure can be avoided, which means that the callee do have the ability to know which generic parameters were employed during the call.
Please have a look at: Using TypeTokens to retrieve generic parameters
Thanks
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.
精彩评论