Java 1.8之后,新增了一個叫做SerializedLambda
的類,它用來保存Lambda表達式序列化之后的數據,通過SerializedLambda
可以獲取Lambda表達式的各種元信息,包括參數類型、返回值類型等。
下面的代碼可以獲取一個Lambda表達式對應的SerializedLambda
實例:
interface Function2<R, T1, T2> extends Serializable {
R apply(T1 t1, T2 t2);
}
try {
Function2<String, Integer, Double> lambda = (a, b) -> a + "," + b;
Method method = lambda.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(true);
SerializedLambda serializedLambda = (SerializedLambda) method.invoke(lambda);
System.out.println(serializedLambda);
} catch (Exception e) {
throw new RuntimeException("獲取Lambda信息失敗", e);
}
以上代碼能工作的前提是,用於接收Lambda表達式的函數式接口必須繼承自Serializable
,就像Function2
接口一樣。
SerializedLambda
中的內容十分豐富,包含以下屬性:
public final class SerializedLambda implements Serializable {
/**
* The capturing class.
*/
private final Class<?> capturingClass;
/**
* The functional interface class.
*/
private final String functionalInterfaceClass;
/**
* The functional interface method name.
*/
private final String functionalInterfaceMethodName;
/**
* The functional interface method signature.
*/
private final String functionalInterfaceMethodSignature;
/**
* The implementation class.
*/
private final String implClass;
/**
* The implementation method name.
*/
private final String implMethodName;
/**
* The implementation method signature.
*/
private final String implMethodSignature;
/**
* The implementation method kind.
*/
private final int implMethodKind;
/**
* The instantiated method type.
*/
private final String instantiatedMethodType;
/**
* The captured arguments.
*/
private final Object[] capturedArgs;
}
完整說明可參見JDK源碼的注釋。
要獲取Lambda表達式對應的參數類型和返回值類型,可使用其中的instantiatedMethodType
屬性,它的一般形式如下:
(Ljava/lang/Integer;Ljava/lang/Double;)Ljava/lang/String;
其中,括號里面的是參數類型,括號外面的是返回值類型,每個類型都以L
開頭,以分號結尾。以上字符串表明當前Lambda函數的參數類型是[java.lang.Integer, java.lang.Double]
,返回值類型是java.lang.String
。很容易使用正則表達式解析出對應的信息。
為方便SerializedLambda
的使用,可以將相關方法封裝成一個公共接口:
public interface SerializableLambda extends Serializable {
Pattern RETURN_TYPE_PATTERN = Pattern.compile("\\(.*\\)L(.*);");
Pattern PARAMETER_TYPE_PATTERN = Pattern.compile("\\((.*)\\).*");
default SerializedLambda getSerializedLambda() {
try {
Method method = getClass().getDeclaredMethod("writeReplace");
method.setAccessible(true);
return (SerializedLambda) method.invoke(this);
} catch (Exception e) {
throw new RuntimeException("獲取Lambda信息失敗", e);
}
}
/**
* 獲取Lambda表達式返回類型
*/
default Class<?> getReturnType() {
String expr = getSerializedLambda().getInstantiatedMethodType();
Matcher matcher = RETURN_TYPE_PATTERN.matcher(expr);
if (!matcher.find() || matcher.groupCount() != 1) {
throw new RuntimeException("獲取Lambda信息失敗");
}
String className = matcher.group(1).replace("/", ".");
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
throw new RuntimeException("無法加載類", e);
}
}
/**
* 獲取Lambda表達式的參數類型
*/
default List<Class<?>> getParameterTypes() {
String expr = getSerializedLambda().getInstantiatedMethodType();
Matcher matcher = PARAMETER_TYPE_PATTERN.matcher(expr);
if (!matcher.find() || matcher.groupCount() != 1) {
throw new RuntimeException("獲取Lambda信息失敗");
}
expr = matcher.group(1);
return Arrays.stream(expr.split(";"))
.filter(s -> !s.isBlank())
.map(s -> s.replace("L", "").replace("/", "."))
.map(s -> {
try {
return Class.forName(s);
} catch (ClassNotFoundException e) {
throw new RuntimeException("無法加載類", e);
}
})
.collect(Collectors.toList());
}
}
只需讓接收Lambda表達式的函數式接口繼承自SerializableLambda
,就可以方便地獲取Lambda表達式的參數類型和返回值類型,且可適用於任意形式的Lambda表達式,包括箭頭函數的形式和方法引用的形式。
測試代碼如下:
interface Function2<R, T1, T2> extends SerializableLambda {
R apply(T1 t1, T2 t2);
}
class A {
public A(Integer num, String str) {
}
}
public class Main {
public static void main(String[] args) {
testSerializableLambda((Integer a, Double b) -> a + ',' + b);
testSerializableLambda(Main::div);
testSerializableLambda(A::new);
}
private static <R, T1, T2> void testSerializableLambda(Function2<R, T1, T2> lambda) {
System.out.println("參數類型:" + lambda.getParameterTypes());
System.out.println("返回值類型:" + lambda.getReturnType());
}
private static double div(int a, int b) {
return a * 1.0 / b;
}
}
輸出結果:
參數類型:[class java.lang.Integer, class java.lang.Double]
返回值類型:class java.lang.Double
參數類型:[class java.lang.Integer, class java.lang.Integer]
返回值類型:class java.lang.Double
參數類型:[class java.lang.Integer, class java.lang.String]
返回值類型:class byx.test.A