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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM