javagenericsreflectionparameterized-types

Reflection check if parameterized type is String and Object


I am having an issue when checking parameterized types of a parameter.

I am using method.getGenericParameterTypes() and casting it to ParameterizedType simply to check if the parameter is the correct Map however when I check the types I get String, ? instead of String, Object, for the string it's easy to check with String.class.equals(type[0]) however for ? doing Object.class.equals(type[1]) will return false.

Is there a better way to check if the parameterized type is Object?


Solution

  • The parameterized type is ?, not Object. These are not at all the same thing.

    You can call map.put("Hello", new Object()); on a Map<String, Object>. It is not possible to call map.put at all on a Map<String, ?>*.

    You can pass an expression of type Map<String, Integer> as a parameter to a method, if it wants a Map<String, ?>. You can't pass a Map<String, Object>. You also can't pass a Map<String, Integer> to a method that wants a Map<String, Object> (because if you could, you can then put a new Object() in there, and break everything).

    Note furthermore that your plan is very fragile and doesn't cover most of the cases that generics are trying to cover. For example, if you have a:

    public class Foo<T> {
        private final Map<T, Object> map = new HashMap<>();
    }
    

    Then for, say, new Foo<String>(), the map is in fact the one you wanted, but your check will find T, which obviously isn't String. Generics aren't reified. You can't hack around it with .getGenericType() - whatever you're trying to do is probably never going to work and you need to rethink your approach.

    A ? will be an instance of WildcardType; cast it to that, then ask for the lower bounds. Object should be amongst them (for just ?, that'll be the only lower bound, in fact). There's not a lot you can do other than do an instanceof check for all the many different kinds of types that exist and write custom code for each and every one. You'll find that your question ('can I pass new Object() without breaking things, for example') is unanswerable for some of these, such as when it's an instance of java.lang.reflect.TypeVariable.

    *) Except for the academic case of map.put("Hello", null); which is surely the desired call to make.