Search code examples
javagenericstype-bounds

Generics: Compiler seems incapable of recognizing that the type passed to an argument is the same as what is returned - why?


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>?


Solution

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