zoukankan      html  css  js  c++  java
  • 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
    
  • 相关阅读:
    185. [USACO Oct08] 挖水井
    JavaEE Tutorials (9)
    vjudge A
    HDU 2089 不要62
    国庆 day 2 下午
    国庆 day 2 上午
    国庆 day 1 下午
    P2899 [USACO08JAN]手机网络Cell Phone Network
    洛谷 P1690 贪婪的Copy
    洛谷 P2209 [USACO13OPEN]燃油经济性Fuel Economy
  • 原文地址:https://www.cnblogs.com/baiyuxuan/p/15680120.html
Copyright © 2011-2022 走看看