Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 976
whitehat Avatar asked Jan 05 '12 11:01

whitehat


People also ask

Is it necessary to use T as the name of a type parameter?

Yes, it is mandatory to specify it. If you remove it and its usage in generic classes and methods they will become normal classes and methods.

How do you define a generic class in Java?

A Generic class simply means that the items or functions in that class can be generalized with the parameter(example T) to specify that we can add any type as a parameter in place of T like Integer, Character, String, Double or any other user-defined type.

What are generics in Java?

Generics means parameterized types. The idea is to allow type (Integer, String, … etc., and user-defined types) to be a parameter to methods, classes, and interfaces. Using Generics, it is possible to create classes that work with different data types.


2 Answers

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.

like image 125
yankee Avatar answered Sep 20 '22 01:09

yankee


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);
like image 41
Peter Lawrey Avatar answered Sep 19 '22 01:09

Peter Lawrey