views:

75

answers:

3

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

A: 

I think this should explain why the constraint is not doing what you expect:

http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ206

Tetsujin no Oni
I'm afraid it doesn't answer the question. I'm not attempting to access `T.class`; my question is why the compiler doesn't recognize that the type bound `<T extends BaseObject>` means that the `T` in my input parameter `Class<T>` will refer to the same type as the `T` in the return type `GenericDao<T>`.
matt b
+4  A: 

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.

erickson
This makes perfect sense, thanks. I forgot that getClass() will return a wildcard type
matt b
+2  A: 

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.

Gray