我在使用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