cyberoblivion
cyberoblivion

Reputation: 719

java generic type parameter with newInstance

I am not sure why Java is requiring me to cast the return of the makeInstance method to T? What am I doing wrong? Is there a better way to accomplish this?

public class Scratch {

    public <T extends Base> T freshen(T instance) {
        //why do I need to cast this to T
        return makeInstance(instance.getClass());
    }

    public <T extends Base> T makeInstance(Class<T> type) {
        try {
            return type.newInstance();
        } catch (InstantiationException ex) {
            Logger.getLogger(Scratch.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(Scratch.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public static final class Base {

    }

}

Upvotes: 3

Views: 1035

Answers (3)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726929

The reason Java wants you to cast the result is that T returned from makeInstance method is not guaranteed to be the same as T of freshen method. The reason for it is that getClass() does not return Class<T>. Instead, it returns Class<? extends X>, where X is the erasure of the static type of instance, i.e. Object. That's where the compiler's chain of inference stops: it cannot infer from that call that the return type would be T, requiring you to do the cast.

An alternative to casting the instance would be casting the class, like this:

return makeInstance((Class<T>)instance.getClass());

Upvotes: 2

Itay Maman
Itay Maman

Reputation: 30733

The root cause for your problem is "type erasure": when the Java compiler compiles the body of the freshen() method it essentially replaces the T type with its upper bound, namely: Base.

So here's the reasoning the compiler is doing: - instance is of type Base (as I said, T was replaced with Base) - .getClass() is invoked on a variable of type Base so the return value of this call is Class<? extends Base> - the parameter that is passed to makeInstance() is therefore, from the compiler's point of view, of type Class<? extends Base>. Consequently the T inside makeInstance() is also Base => the method returns Base

Upvotes: 1

Tanmay Patil
Tanmay Patil

Reputation: 7057

You can solve this problem using following.

public class Scratch<T extends Base> {

    public T freshen(T instance) {
        // No need to cast this to T
        return makeInstance(instance.getClass());
    }

    public T makeInstance(Class<T> type) {
        try {
            return type.newInstance();
        } catch (InstantiationException ex) {
            Logger.getLogger(Scratch.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            Logger.getLogger(Scratch.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public static final class Base {

    }

}

Hope this helps.

Upvotes: 0

Related Questions