问题
Eclipse says that the instanceof operation is not allowed with Type Parameter due to generic type eraser.
I agree that at runtime, no type information stays. But consider the following generic declaration of class :
class SomeClass<T>{
T t;
SomeClass(Object o){
System.out.println(o instanceof T); // Illegal
}
}
At runtime, no T would be present! But if I instantiate this class of type Integer, then the corresponding object will have a field t of type Integer.
Then, why can't I check the type of a variable with T which can be replaced by Integer at runtime. And I would be actually doing something like "o instanceof Integer".
Under which cases, allowing instanceof with a Type Parameter can cause trouble so that it is prohibited?
回答1:
If you need T at runtime, you need to provide it at runtime. This is often done by passing the Class<T> which T has to be.
class SomeClass<T> {
final T t;
public SomeClass(Class<T> tClass, T t) {
if(!tClass.isAssignableFrom(t.getClass()) throw new IllegalArgumentException("Must be a " + tClass);
this.t = t;
}
private SomeClass(T t) {
this.t = t;
}
public static <T> SomeClass<T> of(Class<T> tClass, T t) {
if(!tClass.isAssignableFrom(t.getClass()) throw new IllegalArgumentException("Must be a " + tClass);
return new SomeClass(t);
}
}
// doesn't compile
SomeClass<Integer> intSomeClass = SomeClass.of(Integer.class, "one");
Class clazz = Integer.class;
// compiles with a warning and throws an IAE at runtime.
SomeClass<Integer> intSomeClass = (SomeClass<Integer>) SomeClass.of(clazz, "one");
// compiles and runs ok.
SomeClass<Integer> intSomeClass = SomeClass.of(Integer.class, 1);
回答2:
But if I instantiate this class of type Integer, then the corresponding object will have a field t of type Integer
No, it won't. It will have a field of type Object. Just everytime you access it, it will be cast to an Integer.
Consider the following code:
SomeClass<Integer> c = new SomeClass<Integer>();
SomeClass untyped = (SomeClass)c; // Which type was it?
SomeClass<String> stringTyped = (SomeClass<String>)untyped; // Now it's STRING??
Works. Gives you a bunch of compiler warnings, but works. Because the field T is actually of type Object and can be cast to anything.
回答3:
After compiling statement o instanceof T
would be o instanceof Object
and because all types derives from Object, it will always evaluate to true. Allowing this kind of tests would give false positive results
回答4:
Because of type erasure, this never works. At runtime, you only know that your class has a type parameter T
, but not which type it is for a given instance. So you can't determine whether an object is of type T
to begin with, because you don't know what T
is, not because it would cause some sort of trouble.
If you need to do this sort of runtime check, pass a type token to your object explicitly:
SomeClass(Object o, Class<T> type) {
System.out.println(type.isInstance(o));
}
回答5:
But if I instantiate this class of type Integer, then the corresponding object will have a field t of type Integer.
Actually, it wouldn't. It'd have a field t
of type Object
. As you've said, generics are almost entirely syntactic sugar (the exception is that when you extend a generic class and specify a type parameter, the type remains as metadata in the class file).
回答6:
The generic type arguments are not known at runtime, so there is no class you can compare with. T is only known at compile time. Generics do only help the developer to write code easier. But at runtime, the arguments are just Object
instances.
回答7:
Java realize its Generics using "Erasure", it can check the type and delete the type parameter information in "COMPILE TIME", in "RUN TIME" there will only the BOUNDARIES of the type parameters, so there will not be any thing like "Integer"
来源:https://stackoverflow.com/questions/8741984/why-t-instanceof-t-is-not-allowed-where-t-is-a-type-parameter-and-t-is-a-varia