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 con
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));
}
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);
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
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).
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.
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.