TypeTools java反射获取类、接口、lanmda匿名内部类、方法引用的泛型真实类型的工具类

673 阅读2分钟

TypeTools


一个非常厉害的库,可以解析大部分的泛型信息了,包括很难取到的lambda表达式匿名内部类的泛型结果

官方文档👉Github - TypeTools

<dependency>
    <groupId>net.jodah</groupId>
    <artifactId>typetools</artifactId>
    <version>0.6.3</version>
</dependency>

非常轻量,整个依赖只有两个java文件

image.png

获取一般泛型的方法

一般来说,类、字段、方法参数上的具体泛型可以通过简单的反射获取,想获取一个类其实现接口的泛型class Foo implements Bar<String>,或者某个字段List<String>的 泛型String, ,获取实现的接口泛型信息用.getGenericInterfaces()、继承用.getGenericSuperclass(),然后将其中元素转换为ParameterizedType再使用.getActualTypeArguments() 就可以获取泛型参数了

@Test
void getActualTypeArguments() {
    Type[] actualTypeArguments = ((ParameterizedType) Foo.class.getGenericInterfaces()[0]).getActualTypeArguments();
    System.out.println(Arrays.toString(actualTypeArguments));
    Bar<String> bar = new Bar<String>() {
            @Override
            public void run(String s) {
            }
        };
    actualTypeArguments = ((ParameterizedType) bar.getClass().getGenericInterfaces()[0]).getActualTypeArguments();
    System.out.println(Arrays.toString(actualTypeArguments));
}
interface Bar<T>{void run(T t);}
static class Foo implements Bar<String> {
    @Override
    public void run(String s) {
        
    }
}

这个方法直到匿名内部类都是可以用的,但是对于强迫症患者点了一下idea提示,把匿名内部类变成lambda的时候,就无法使用此方法获取泛型参数了,因为lambda会把泛型变为<?> ,但是这个工具厉害的地方就是对于lambda依然能够获取

TypeTools 获取泛型

Function<String, Integer> strToInt = Integer::valueOf;
Class<?>[] typeArgs = TypeResolver.resolveRawArguments(Function.class, strToInt.getClass());
System.out.println(Arrays.toString(typeArgs));

lambda 甚至是方法引用,都可以获取到泛型参数

image.png

  • Type reify(Type type, Class<S> context) 使用来自 context 的类型变量信息返回一个完全具体化的 type.
  • Type reify(Type genericType) 使用泛型声明中的信息返回完全具体化的genericType.
  • Class<?>[] resolveRawArguments(Class<T> type, Class<S> subType) 使用来自subType的类型变量信息解析type的原始参数.
  • Class<?> resolveRawArgument(Class<T> type, Class<S> subType) 使用来自 subType 的类型变量信息解析 type 的原始参数.
  • Type resolveGenericType(Class<?> type, Type subType) 使用来自 subType 的类型变量信息解析泛型 type.
  • Class<?> resolveRawClass(Type genericType, Class<?> subType) 使用来自subType的类型变量信息解析genericType的原始类.
interface Foo<T> {}
class Bar implements Foo<List<Integer>> {}

Type typeArgs = TypeResolver.reify(Foo.class, Bar.class);

ParameterizedType paramType = (ParameterizedType) typeArgs;
Type[] actualTypeArgs = paramType.getActualTypeArguments();
ParameterizedType arg = (ParameterizedType)actualTypeArgs[0];

assert paramType.getRawType() == Foo.class;
assert arg1.getRawType() == List.class;
assert arg1.getActualTypeArguments()[0] == Integer.class;

不过由于java本身的限制或者太过困难,尽管这个工具很强大了,但也不是所有泛型都能获取的

  • 如果接口只定义泛型,但是放着完全不用,那就是取不到的
public interface Foo<A,B,C,D>{
    void run(A a);
}

@Test
void test_2021_12_04_22_22_33() {
    Foo<String,Integer,Boolean,Character> foo = a->{};
    System.out.println(Arrays.toString(TypeResolver.resolveRawArguments(Foo.class, foo.getClass())));
}

输出:

[class java.lang.String, 
class net.jodah.typetools.TypeResolver$Unknown, 
class net.jodah.typetools.TypeResolver$Unknown, 
class net.jodah.typetools.TypeResolver$Unknown]
  • 或者用到泛型的地方又套了一个泛型对象 issures 58
public interface Foo<A,B>{
    List<B> run(A a);
}

@Test
void test_2021_12_04_22_22_33() {
    Foo<String,Integer> foo = a->new ArrayList<>();
    System.out.println(Arrays.toString(TypeResolver.resolveRawArguments(Foo.class, foo.getClass())));
}
[class java.lang.String, 
class net.jodah.typetools.TypeResolver$Unknown]