Search code examples
javagenericsreflectionconstructorintrospection

Checking at runtime if a class has a specific constructor that is using generics


Hello all :) I'm trying to chose the right constructor in a class. Here is the code:

Constructor[] constructors = targetClass.getConstructors();
Constructor goodConstructor = null;
for (Constructor constructor : constructors) {
    Class[] parameterTypes = constructor.getParameterTypes();
    if (parameterTypes.length = 1 && parameterTypes[0].equals(Map.class)) {//here
        goodConstructor = constructor;
    }
}

I want to switch from Map.class to Map<String, String>.class. I vaguely remember that generics are for compile time only, so this is why the compiler is complaining. How can I check at runtime that the class has the right constructor?

Best regards


Solution

  • You want to use getGenericParameterTypes() instead:

    public class FindConstructor {
    
        public static void main(String[] args) throws IOException {
            for (Constructor<?> constructor : MyClass.class.getConstructors()) {
                Type[] parameterTypes = constructor.getGenericParameterTypes();
                if (parameterTypes.length == 1 && parameterTypes[0] instanceof ParameterizedType) {
                    ParameterizedType parameterizedArg = (ParameterizedType) parameterTypes[0];
                    if (parameterizedArg.getRawType() != Map.class) {
                        continue;
                    }
    
                    if (parameterizedArg.getActualTypeArguments()[0] != String.class) {
                        continue;
                    }
    
                    if (parameterizedArg.getActualTypeArguments()[1] != String.class) {
                        continue;
                    }
                }
                System.out.println("found constructor " + constructor);
            }
        }
    }
    
    class MyClass {
        public MyClass(Map<String, String> map) {
        }
    }
    

    Now if you change MyClass() to take a Map<String, Integer> it will no longer match.

    This becomes much easier with Guava's TypeToken, which utilizes an anonymous class to create a parameterized Type that we can compare to.

    Type mapStringString = new TypeToken<Map<String, String>>(){}.getType();
    for (Constructor<?> constructor : MyClass.class.getConstructors()) {
        Type[] parameterTypes = constructor.getGenericParameterTypes();
        if (parameterTypes.length == 1 && parameterTypes[0].equals(mapStringString)) {
            System.out.println("found constructor " + constructor);
        }
    }