简体   繁体   中英

generic wildcard is accepted but not Type T

For below cases, assuming AModel.class extends ABCModel, The case 1

case 1 works

public static Class<? extends ABCModel> getModel(String objectModel) {

                if(objectModel.equalsIgnoreCase("")) {
                    return AModel.class;
                }

    return null;

    }

case 2 (throws compilation error)

public  static <T extends ABCModel> Class<T> getModel(String objectModel) {

            if(objectModel.equalsIgnoreCase("")) {
                return AModel.class;
            }

return null;

}

Is the purpose is not just the same ?

In the second case you're constraining the type variable T to extend ABCModel and promising to return Class<T> . However the T doesn't get bound anywhere, so during compile-time there's no way to determine what T is.

The difference in example 1 is that the return type is defined to be Class<? extends ABCModel> Class<? extends ABCModel> , which has an upper bound of ABCModel .

One way to bind T during compile-time in your second case is to give it as a parameter to the method, for example:

public static <T extends ABCModel> Class<T> getModel(String objectModel, Class<T> clazz) {

Now T is bound based on the parameter passed into the method (which is why you see a lot of generic methods taking in a Class parameter).

Of course this won't help with your method since you want to retrieve the Class , which is unnecessary if you already have it to pass in. But that just means that you should either use example 1 or fix your design.

The two are very different.

In the first case, you are returning some class that extends ABCModel , that you decide . The caller cannot assume anything about the identity of the type parameter.

In the second case, you are promising to return the class T , for whatever T the caller wants! The caller can call your method with T being whatever it wants , and your method has to magically return a class of that type parameter, in this case, without knowing what T is at all. That means your method must simultaneously return a Class<AModel> and Class<BModel> , etc. with the same code. That is clearly impossible (unless it always returns null ).

The solution to your problem depends on your use case:

If your method determines a specific type and returns it, your first example is the correct approach. There is no use trying to specify the return type further, as the question "Which subtype" cannot by answered if the caller needs to specify that type.

If however, there are several invocations of your method, and each of those will always return the expected subtype, the generic return type is useful. In that case, your caller will know the specific return type and can use the parameter to avoid a cast on the calling site. All you need to do is include that cast in your implementation: return (Class<T>) AModel.class;

First case example:

// The caller does not know the return type, so a simple unknown type is appropriate
Class<?> determineType(Object myObject) {
  return myObject.getClass(); 
}

Second case example:

// The caller will already know that a property named 'street' will be of type String, so he can invoke <String>getTypedProperty("street") and avoid having to cast himself
<T> T getTypedProperty(String name) {
  return (T) genericPropertyStore.get(name);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM