Java中获取lambda表达式的泛型类型

假设有以下接口:

public interface Factory<T> {
    T create();
}

这是一个泛型接口,在实现Factory的时候需要指定泛型参数:

public class StringFactory implements Factory<String> {
    @Override
    public String create() {
        return "hello";
    }
}

public class IntegerFactory implements Factory<Integer> {
    @Override
    public Integer create() {
        return 123;
    }
}

假如我们要获取一个Factory实例的泛型参数,要怎么做呢?可以使用Java反射API提供的函数getGenericInterfaces

public class Main {
    public static void main(String[] args) {
        System.out.println(getFactoryTypeParameter(new StringFactory()));
        System.out.println(getFactoryTypeParameter(new IntegerFactory()));
    }

    // 获取接口的泛型参数
    private static Class<?> getFactoryTypeParameter(Factory<?> factory) {
        return (Class<?>) ((ParameterizedType) factory.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
    }
}

输出结果:

class java.lang.String
class java.lang.Integer

一切看起来很完美,但是,假如我们传递一个lambda表达式给getFactoryTypeParameter函数会怎么样呢?

System.out.println(getFactoryTypeParameter(() -> 3.14));

很不幸,控制台出现了异常:

Exception in thread "main" java.lang.ClassCastException: class java.lang.Class cannot be cast to class java.lang.reflect.ParameterizedType (java.lang.Class and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')
	at byx.test.genetic.Main.getFactoryTypeParameter(Main.java:14)
	at byx.test.genetic.Main.main(Main.java:9)

在我们的印象中,lambda表达式就是匿名内部类的一种简化写法,我们尝试一下将lambda表达式还原成匿名内部类:

System.out.println(getFactoryTypeParameter(new Factory<Double>() {
    @Override
    public Double create() {
        return 3.14;
    }
}));

令人惊讶的是,此时getFactoryTypeParameter又正常工作了,控制台输出:

class java.lang.Double

从以上可以看出,匿名内部类和lambda表达式还是有本质上的区别的,因为getGenericInterfaces函数对lambda表达式无效,而对匿名内部类有效。这里不深究造成这种差异的具体原因,而是只给出解决方案。

首先新建一个TypeRef类:

public abstract class TypeRef<T> {
    protected TypeRef(Factory<T> factory) {}

    public Class<?> getGenericType() {
        return (Class<?>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }
}

TypeRef是一个抽象类,而且带一个泛型参数T,在它的构造方法中我们需要传入一个Factory的实例,这个实例主要用来帮助TypeRef确定T的类型。

TypeRef中还有一个getGenericType方法,它使用了Java的getGenericSuperclassAPI,这个函数可以用来获取抽象父类的泛型参数。

我们需要像下面这样使用TypeRef

public class Main {
    public static void main(String[] args) {
        System.out.println(getFactoryTypeParameter(new TypeRef<>(() -> 3.14){}));
    }
    
    // 获取接口的泛型参数(使用TypeRef)
    private static <T> Class<?> getFactoryTypeParameter(TypeRef<T> typeRef) {
        return typeRef.getGenericType();
    }
}

new TypeRef<>(() -> 3.14){}这行代码实际上原地创建了一个TypeRef的实现类,编译器可以根据类型推导得出其泛型参数为Double,因此可以间接利用TypeRef来得到lambda表达式的泛型参数,这就是改进后的getFactoryTypeParameter的主要逻辑。

运行以上代码,正确地输出了结果:

class java.lang.Double

当然,改进后的getFactoryTypeParameter对于之前的StringFactoryIntegerFactory也适用:

System.out.println(getFactoryTypeParameter(new TypeRef<>(new StringFactory()){}));
System.out.println(getFactoryTypeParameter(new TypeRef<>(new IntegerFactory()){}));

输出如下:

class java.lang.String
class java.lang.Integer
posted @ 2021-12-12 18:40  baiyuxuan  阅读(924)  评论(0编辑  收藏  举报