Is there a better (more safe) way to get the class of the parameterized type?

Viewed 88

I want to create a method that returns the class of parameterized type.

Consider an interface:

private static interface MyInterface<T> {
    void run(T parameter);
}

And one implementation:

private static class MyInterfaceImplString implements MyInterface<String> {
    @Override
    public void run(String parameter) {     }
}

Now, I want to pass MyInterfaceImplString.class to a method and this method will return String.class.

I am printing things around, and I can see the information be there but I am just unable to get it. Or at least with a safer way.

public class TypesTest {

    public static void main(String[] args) {
        Class<?> genericParameter1 = getGenericParamaterType(MyInterfaceImplVoid.class);
        System.out.println(genericParameter1); //expect Void here

        Class<?> genericParameter2 = getGenericParamaterType(MyInterfaceImplString.class);
        System.out.println(genericParameter2); //expect String here
    }

    private static Class<?> getGenericParamaterType(Class<? extends MyInterface<?>> clazz) {
        for (Method m : clazz.getMethods()) {
            if ("run".equals(m.getName()) && m.getParameterCount() == 1) {
                System.out.println(TypeLiteral.get(clazz).getParameterTypes(m));
            }
        }

        return null;
    }

    private static class MyInterfaceImplVoid implements MyInterface<Void> {

        @Override
        public void run(Void parameter) {           }
    }

    private static class MyInterfaceImplString implements MyInterface<String> {

        @Override
        public void run(String parameter) {         }

    }

    private static interface MyInterface<T> {
        void run(T parameter);
    }
}

Ignore the null return value in the method. I just want to print things, see what I get and then return it. However, my current implementation seems kind of unorthodox because the class might have more than one run method that has nothing to do with MyInterface.

In case of a XY problem, I want be to able to recognize which of these implementations contain a parameterized type that extends Employee (let's say). I call the run method of the interface indirectly given an HourlyPaidEmployee or a MonthlyPaidEmployee. So, if the underlying implementation is MyInterface<Employee>, I can inject my actual employee (either is monthly or hourly paid). But if the implementation is MyInterface<HourlyEmployee>, I cannot inject a monthly paid one. So, getting the class of the parameterized type, helps me know what types of Employees I can inject safely to run method.

I am with java-8 and Guice dependency in classpath, which contains guava dependency.

EDIT: @Glorfindel made good point in his answer. But if the implementation looks like:

private static class MyInterfaceImplVoid implements MyInterface<Void> {

    @Override
    public void run(Void parameter) {
    }

    public void run(Integer par) {
    }
}

I am not able to know what is the method that refers to the interface, hence his answer might return to me Integer.cass.

4 Answers

You should not deal with methods when you want to find out the actual type arguments of the type. You can implement it for the scenario as simple as your using

public class TypesTest {
    public static void main(String[] args) {
        Type genericParameter1 = getGenericParameterType(MyInterfaceImplVoid.class);
        System.out.println(genericParameter1+", "+(genericParameter1==Void.class));

        Type genericParameter2 = getGenericParameterType(MyInterfaceImplString.class);
        System.out.println(genericParameter2+", "+(genericParameter2==String.class));
    }

    private static Type getGenericParameterType(Class<? extends MyInterface<?>> clazz) {
        for(Type interfaceType: clazz.getGenericInterfaces()) {
            if(interfaceType == clazz)
                throw new IllegalArgumentException("raw implementation");
            if(interfaceType instanceof ParameterizedType) {
                ParameterizedType pt = (ParameterizedType)interfaceType;
                if(pt.getRawType() == MyInterface.class)
                    return pt.getActualTypeArguments()[0];
            }
        }
        throw new UnsupportedOperationException("not implemented directly");
    }
}
class java.lang.Void, true
class java.lang.String, true

But note that the return type is Type, not class, as the type argument is not guaranteed to be a Class (a reifiable type or a raw type). It could be a parameterized type on its own, but also a type variable.

Consider a case like

abstract class InTheMiddle<T> implements MyInterface<T> {}
class Implementation extends InTheMiddle<String> {
    @Override
    public void run(String parameter) {
    }
}

This would be solvable be traversing the type hierarchy, querying the declared type variables and match them with the actual type arguments when they appear at the target type variable you want to resolve.

But mind that the following also is possible:

abstract class Outer<T> {
    class Inner implements MyInterface<T> {
        @Override
        public void run(T parameter) {
        }
    }
}
class SubclassOfOuter extends Outer<Integer> {
}
SubclassOfOuter outer = new SubclassOfOuter();
MyInterface<Integer> object = outer.new Inner();

which would require traversing the type hierarchy and the artifacts of the lexical scope (besides outer classes, implementation classes can be local classes of a generic method placed inside a generic class that may be local to another method…).

Then, there are the constructs that are impossible to query at runtime at all, due to type erasure:

class GenericImpl<X> implements MyInterface<X> {
    @Override
    public void run(X parameter) {
    }
}
GenericImpl<String> a = new GenericImpl<>();
GenericImpl<Integer> b = new GenericImpl<>();

or

MyInterface<Thread> another = t -> {};

So think twice before you make your code dependent on the ability to determine the actual type argument at runtime.

If I replace

System.out.println(TypeLiteral.get(clazz).getParameterTypes(m));

with

return m.getParameterTypes()[0];

I get the following output:

class java.lang.Void  
class java.lang.String

No need for Guice/Guava, plain old Java seems to work fine. I do not know a good solution for the other problem; it seems reflection isn't even able to detect that one method is an override and the other one is not. If you have sufficient control over the implementation classes, I'd advice simply to use another name instead of run for the other method.

Your T is unbound.

That means every impl of this interface, even your MyInterfaceImplVoid, will have a run(Object) method, even if you didn't write one at all - and it will cast the argument to in this case Void and pass the call onwards to run(Void). So, if you want to be sure you call the interface method, always call run(Object), and send an object of the right type (as figured out via @Glorfindel 's answer).

Note that this 'trick' of figuring out what the bound is usually a bad plan. What are you going to do when I write this class:

class DynamicMyInterface<Z> implements MyInterface<Z> {
    private Class<Z> forcedType;

    public DynamicMyInterface(Class<Z> type) {
        this.forcedType = type;
    }

    public void run(Z in) {
        forcedType.cast(in);
        ....
    }
}

Your jaunt into .getGenericType() and such will never be able to figure out what it is, because of erasure. You won't get any further than Z extends Object.

I found a way to get the class of the parameterized type with this "ugly" - "what the heck" method:

private static Class<?> getGenericParamaterType(Class<? extends MyInterface<?>> clazz) {
    for (TypeToken<?> typeToken : TypeToken.of(clazz).getTypes()) {
        Class<?> rawType = typeToken.getRawType();
        if (rawType == MyInterface.class) {
            TypeLiteral<?> typeLiteral = TypeLiteral.get(typeToken.getType());
            for (Method method : rawType.getMethods()) {
                if ("run".equals(method.getName())) {
                    TypeLiteral<?> typeLiteral2 = typeLiteral.getParameterTypes(method).get(0);
                    return typeLiteral2.getRawType();
                }
            }
        }
    }
    return null;
}

It works exactly as I want. However, I am not sure for its performance. Plus, I really think that Guava provides a more concise way to achieve what I want. In the other hand, I was not able to find it.

Below is the full example that shows that it works. Even in complex situations where multiple run methods are implemented by the implementation and even in abstract classes that do not implement the method.

public class TypesTest {

    public static void main(String[] args) {
        Class<?> genericParameter1 = getGenericParamaterType(MyInterfaceImplVoid.class);
        System.out.println(genericParameter1); //expect Void here

        Class<?> genericParameter2 = getGenericParamaterType(MyInterfaceImplString.class);
        System.out.println(genericParameter2); //expect String here

        Class<?> genericParameter3 = getGenericParamaterType(AbstractImpl.class);
        System.out.println(genericParameter3); //expect Integer here
    }

    private static Class<?> getGenericParamaterType(Class<? extends MyInterface<?>> clazz) {
        for (TypeToken<?> typeToken : TypeToken.of(clazz).getTypes()) {
            Class<?> rawType = typeToken.getRawType();
            if (rawType == MyInterface.class) {
                TypeLiteral<?> typeLiteral = TypeLiteral.get(typeToken.getType());
                for (Method method : rawType.getMethods()) {
                    if ("run".equals(method.getName())) {
                        TypeLiteral<?> typeLiteral2 = typeLiteral.getParameterTypes(method).get(0);
                        return typeLiteral2.getRawType();
                    }
                }
            }
        }
        return null;
    }

    private static class MyInterfaceImplVoid extends JPanel implements MyInterface<Void>, Runnable {

        @Override
        public void run(Void parameter) { 
        }

        public void run(Integer par) {          }

        public void run(String s) {         }

        @Override
        public void someOtherMethod() {         }

        @Override
        public void run() {         }

    }

    private static abstract class AbstractImpl implements MyInterface<Integer> {

    }

    private static class MyInterfaceImplString implements MyInterface<String> {

        @Override
        public void run(String parameter) {         }

        @Override
        public void someOtherMethod() {         }

    }

    private static interface MyInterface<T> {
        void run(T parameter);

        void someOtherMethod();
    }
}

I will not mark this answer as accepted since I am quite sure there is a more convenient way to do it.

Related