Reputation: 139961
Let's say I have several POJOs which all extend a common supertype, BaseObject
.
I have a GenericDao
which is declared as public interface GenericDao<T>
.
For each type-specific DAO, I have an interface which extends the generic type and restricts it to a concrete type (public interface UserDao extends GenericDao<User>
) and then an implementation of the type-specific DAO.
In a class that attempts to use a number of GenericDao
implementations, I have a method that looks like
public <T extends BaseObject> long create(T object) {
return getDao(object.getClass()).save(object);
}
If I implement getDao()
so that it's parameter is a Class
object, such as
private <T extends BaseObject> GenericDao<T> getDao(Class<T> clazz) { ... }
Then the call to getDao(object.getClass()
in the create()
method fails to compile - the compiler appears to interpret the return type of getDao()
as
GenericDao<? extends BaseContractObject>
rather than recognizing that getDao(Class<T>)
is going to return me a GenericDao
of the same type T
.
Can someone explain why this is? I understand that repeated appearances of the same type bound or wildcard don't necessary refer to the same type; however it seems like the compiler should recognize from the signature of getDao(Class<T>)
that the T passed in should be the same T returned (but obviously it isn't capable of recognizing this, the why is the part I fail to grasp).
If I instead define getDao
's signature to be
private <T extends BaseContractObject> GenericDao<T> getDao(T obj) { ... }
Then there is no issue in compiling a create()
implementation which looks like
public <T extends BaseContractObject> long create(T object) {
return getDao(object).save(object);
}
So why is the compiler capable of recognizing in this case that the T
argument passed to getDao(T)
is the same T
in the return type, whereas it couldn't recognize this when the argument was Class<T>
?
Upvotes: 1
Views: 703
Reputation: 116908
This is a classic type erasure issue. getClass()
has the following signature:
public final native Class<? extends Object> getClass();
If you have a String
and do a getClass()
on it, the class you get is Class<? extends String>
. The javadocs read:
* @return The <code>java.lang.Class</code> object that represents
* the runtime class of the object. The result is of type
* {@code Class<? extends X>} where X is the
* erasure of the static type of the expression on which
* <code>getClass</code> is called.
You will need to force the following cast to get it to work:
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>)object.getClass();
return getDao(clazz).save(object);
That works for me.
Upvotes: 2
Reputation: 269797
The expression object.getClass()
, where object is of type T extends BaseObject
, returns a Class<? extends BaseObject>
, not a Class<T>
as one might expect. So, getDao()
is returning a DAO of the same type it receives; it's just not receiving the expected type.
Upvotes: 5
Reputation: 7375
I think this should explain why the constraint is not doing what you expect:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ206
Upvotes: 0