关于Java中lambda表达式的序列化

我在使用mybatis-plus的时候发现有这样一种写法:

LambdaQueryWrapper<Board> wrapper = new LambdaQueryWrapper<>();
// lambda方法引用
queryWrapper.eq(User::getName, "小明");

那么问题来了? 他是怎么通过 User::getName 来获取到字符串name的呢?

通过查阅资料发现, 他是通过类似下面的代码实现的:

@Data
public class Person {
    private int id;
    private String name;
}
interface Func<T, R> extends Function<T, R>, Serializable {
}

public class BeanUtils {

	 public static void main(String[] args) throws Exception {
        // 保存jvm运行过程中生成的lambda字节码文件到指定路径
        System.getProperties().put("jdk.internal.lambda.dumpProxyClasses", "F:/lambda");

        String getId = BeanUtils.convertToFieldName(Person::getId);
        String getName = BeanUtils.convertToFieldName(Person::getName);
        System.out.println(getId);
        System.out.println(getName);
    }

 	public static <T> String convertToFieldName(IGetter<T> fn) throws Exception {
 		// 获取序列化的lambda
        SerializedLambda lambda = getSerializedLambda(fn);
        // 获取真正实现方法的方法名
        String methodName = lambda.getImplMethodName();
        // 判断方法名前缀是否为is或者get
        String prefix = null;
        if (methodName.startsWith("get")) {
            prefix = "get";
        } else if (methodName.startsWith("is")) {
            prefix = "is";
        }
        if (prefix == null) {
            System.out.println("无效的getter方法: " + methodName);
        }
		// 转换为Java变量命名
        return Introspector.decapitalize(methodName.replace(prefix, ""));
    }
   /**
    * 关键在于这个方法
    */
    public static SerializedLambda getSerializedLambda(Serializable fn) throws Exception {
    // 获取lambad对象中 writeReplace 方法 (下面讲述为什么这个对象里面会存在writeReplace方法)
        Method method = fn.getClass().getDeclaredMethod("writeReplace");
        method.setAccessible(Boolean.TRUE);
        // 执行writeReplace
        SerializedLambda lambda = (SerializedLambda) method.invoke(fn);
        return lambda;
    }
}

运行结果:

id
name

这里比较难理解的是这行代码, fn也就是Person::getName, 他本应该只有一个apply方法, 但是却多了一个writeplace方法

Method method = fn.getClass().getDeclaredMethod("writeReplace");

继续探究下面一段代码

Func<Object, Object> func = o -> null;
System.out.println(Arrays.toString(func.getClass().getDeclaredMethods()));

输出

[public java.lang.Object Test2$$Lambda$1/428746855.apply(java.lang.Object), private final java.lang.Object Test2$$Lambda$1/428746855.writeReplace()]

看来确实多了一个writeReplace方法
我将lambda改为了匿名内部类

Func<Object, Object> func2 = new Func<Object, Object>() {
    @Override
    public Object apply(Object o) {
        return null;
    }
};
System.out.println(Arrays.toString(func2.getClass().getDeclaredMethods()));

输出:

[public java.lang.Object Test2$2.apply(java.lang.Object)]

匿名内部类没有writeReplace, 只有lambda才会生成该方法

我又将Func改为Function

Function<Object, Object> func3 = o -> null;
System.out.println(Arrays.toString(func3.getClass().getDeclaredMethods()));

输出

[public java.lang.Object Test2$$Lambda$2/392292416.apply(java.lang.Object)]

Func和Function唯一的区别就是: Func继承了Serializable, Function 没有;说明只有实现了Serializable接口的lambda才有writeReplace
熟悉Serializable的同学都知道, 对象在序列化的时候如果存在writeReplace方法, 就会序列化writeReplace的返回值
继承了Serializable的lambda会自动生成一个writeReplace方法, 返回SerializableLambda对象, 用于序列化. 通过该对象也可以获取到lambda真正实现的方法的方法名(SerializableLambda.getImplMethodName)
例如: Person::getName得到字符串getName, o -> null得到字符串lambda$test3$3bd5442$1

https://www.jianshu.com/p/6b41a43813c8
https://blog.csdn.net/jiandabang/article/details/104543436
https://yuanyu.blog.csdn.net/article/details/114342481

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值