有没有更好(更安全)的方法来获取参数化类型的类?

Geo*_* Z. 3 java generics reflection guava

我想创建一个返回参数化类型类的方法。

考虑一个interface

private static interface MyInterface<T> {
    void run(T parameter);
}
Run Code Online (Sandbox Code Playgroud)

和一种实现:

private static class MyInterfaceImplString implements MyInterface<String> {
    @Override
    public void run(String parameter) {     }
}
Run Code Online (Sandbox Code Playgroud)

现在,我想传递MyInterfaceImplString.class给一个方法,这个方法将返回String.class.

我正在打印东西,我可以看到那里的信息,但我无法获得它。或者至少有一种更安全的方式。

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

忽略方法中的null返回值。我只想打印东西,看看我得到了什么,然后返回。但是,我当前的实现似乎有点不正统,因为该类可能有多个runMyInterface.

XY 问题的情况下,我希望能够识别这些实现中的哪些包含参数化类型extends Employee(假设)。我run间接调用接口的方法给定 anHourlyPaidEmployee或 a MonthlyPaidEmployee。因此,如果底层实现是MyInterface<Employee>,我可以注入我的实际员工(按月或按小时支付)。但如果实施是MyInterface<HourlyEmployee>,我不能注入每月支付的。因此,获取参数化类型的类,可以帮助我了解可以安全地将哪些类型的员工注入到run方法中。

Guice在类路径中使用 java-8 和依赖项,其中包含guava依赖项。

编辑: @Glorfindel 在他的回答中提出了很好的观点。但是如果实现看起来像:

private static class MyInterfaceImplVoid implements MyInterface<Void> {

    @Override
    public void run(Void parameter) {
    }

    public void run(Integer par) {
    }
}
Run Code Online (Sandbox Code Playgroud)

我不知道引用接口的方法是什么,因此他的回答可能会返回给我 Integer.cass

Hol*_*ger 5

当你想找出的实际类型参数,你不应该与方法处理类型。您可以像使用一样简单地为场景实现它

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");
    }
}
Run Code Online (Sandbox Code Playgroud)
class java.lang.Void, true
class java.lang.String, true
Run Code Online (Sandbox Code Playgroud)

但请注意,返回类型是Type,而不是类,因为不能保证类型参数是 a Class(可具体化的类型或原始类型)。它本身可以是参数化类型,也可以是类型变量。

考虑一个案例

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

这可以通过遍历类型层次结构、查询声明的类型变量并在它们出现在您要解析的目标类型变量中时将它们与实际类型参数匹配来解决。

但请注意,以下也是可能的:

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

这将需要遍历类型层次结构和词法范围的工件(除了外部类,实现类可以是放置在泛型类中的泛型方法的本地类,而泛型类可能是另一个方法的本地类……)。

然后,由于类型擦除,有些结构在运行时根本无法查询:

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

或者

MyInterface<Thread> another = t -> {};
Run Code Online (Sandbox Code Playgroud)

因此,在使代码依赖于在运行时确定实际类型参数的能力之前,请三思。