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