Why “t instanceof T” is not allowed where T is a type parameter and t is a variable?

只谈情不闲聊 提交于 2019-11-30 20:36:26

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);

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.

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

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

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).

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.

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"

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!