Reputation: 719
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
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
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
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