获取 lambda 的详细信息
要说啥?
Java8 新增了 lambda 表达式, 最常见的用法是配合 Stream 做集合操作. 下面是一种类似彩蛋的东西可以妙用到某些场合.
一般用法, 比如下面这样
- Optional.of(1L).ifPresent(number -> {
- System.out.println(number);
- });
或者简化成这样
Optional.of(1L).ifPresent(System.out::println);
有什么办法能获取到
System.out::println
里面的方法名字符串
- String methodName = "println"
- ?
啥效果?
执行代码
- FnConverter<Foo> fnConverter = new FnConverter<>();
- String fieldName = fnConverter.convertFnToString(Foo::getBar);
- System.out.println("方法名:"+fieldName);
输出
方法名: bar
怎么做?
第一步: 定义一个
FunctionalInterface
(敲黑板, 画重点
- extends Serializable
- )
- /**
- * @author Frank
- */
- @FunctionalInterface
- public interface Fn<T> extends Serializable {
- Object apply(T source);
- }
第二布: 准备个类(酱油)
- import lombok.Data;
- /**
- * @author liuyuyu
- */
- @Data
- public class Foo {
- private Integer bar;
- }
第三步: 获取 Fn 的信息的工具类
- import java.beans.Introspector;
- import java.lang.invoke.SerializedLambda;
- import java.lang.reflect.Method;
- /**
- * @author Frank
- */
- public class Reflections {
- private Reflections() {
- }
- public static String fnToFieldName(Fn fn) {
- try {
- Method method = fn.getClass().getDeclaredMethod("writeReplace");
- method.setAccessible(Boolean.TRUE);
- SerializedLambda serializedLambda = (SerializedLambda) method.invoke(fn);
- String getter = serializedLambda.getImplMethodName();
- String fieldName = Introspector.decapitalize(getter.replace("get", ""));
- return fieldName;
- } catch (ReflectiveOperationException e) {
- throw new RuntimeException(e);
- }
- }
- }
画重点
SerializedLambda serializedLambda = (SerializedLambda) method.invoke(fn);
第四步: 写个梨子跑起来
- /**
- * @author liuyuyu
- */
- public class FnConverter<T> {
- public String convertFnToString(Fn<T> fn){
- return Reflections.fnToFieldName(fn);
- }
- public static void main(String[] args) {
- FnConverter<Foo> fnConverter = new FnConverter<>();
- String fieldName = fnConverter.convertFnToString(Foo::getBar);
- System.out.println("方法名:"+fieldName);
- }
- }
- Run
方法名: bar
啥原理?
Serializable 是 Java 对象序列化的接口, 凡是实现这个接口(interface 是继承, 也算)Java 都要提供序列化和反序列化的方法(
ObjectInputStream/ObjectOutputStream
可能会让你想起点什么).
但是 lambda 比较特殊, 它是一个方法, 可以认为是一个动作(或者说是功夫? 比如九阴真经), 没办法直接保存, Java 提供了 SerializedLambda 这个类保存 lambda 的信息.
- public final class SerializedLambda implements Serializable {
- private static final long serialVersionUID = 8025925345765570181L;
- private final Class<?> capturingClass;
- private final String functionalInterfaceClass;
- private final String functionalInterfaceMethodName;
- private final String functionalInterfaceMethodSignature;
- private final String implClass;
- private final String implMethodName;
- private final String implMethodSignature;
- private final int implMethodKind;
- private final String instantiatedMethodType;
- private final Object[] capturedArgs;
- // 省略之后代码
- }
知道了这个隐藏 (彩) 特性(蛋), 我们回头看看刚才黑板上画的重点
- @FunctionalInterface //lambda
- public interface Fn<T> extends Serializable // 序列化接口
两个条件满足
因为这个东西是个隐藏 (彩) 特性(蛋), 我们不能直接获取到 SerializedLambda. 直接上反射!
SerializedLambda serializedLambda = (SerializedLambda) method.invoke(fn);
这样, 我们就可以获取到 lambda 的方法名
还能干啥?
在框架设计的时候, 很多场景要获取类的属性, Java8 以前 API 设计的时候只能用字符串方式, 如果是 Java8, 就可以避免字符串.
大家可以打开 (骗星) 栗子, 直接运行代码感受一下.
Mybatis 通用 Mapper 增强 https://github.com/liuyuyu/weekend
lambda meta 信息获取 demo https://github.com/liuyuyu/java-code-suggest/blob/master/src/main/java/io/github/liuyuyu/lambda/meta/Lambda_Meta.md
来源: https://juejin.im/post/5b0948d06fb9a07aa6323866