
111.png
java8 以后我們?cè)谀承┑胤揭?jiàn)到類似的代碼
new LambdaQueryWrapper<User>().eq(User::getUsername, user.getUsername())
通過(guò)lambda的方法引用為User對(duì)象的字段賦值,原理是什么呢?
接下來(lái)我們通過(guò)一個(gè)demo來(lái)解釋
首先創(chuàng)建一個(gè)java工程,編寫如下接口和類
package lambda;
public class Person {
private int id;
private String name;
// 有參和無(wú)參構(gòu)造函數(shù)
// geeter and setter
}
package lambda;
import java.io.Serializable;
@FunctionalInterface
public interface IGetter<T> extends Serializable {
Object get(T source);
}
package lambda;
import java.io.Serializable;
@FunctionalInterface
public interface ISetter<T, U> extends Serializable {
void set(T t, U u);
}
package lambda;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
public class BeanUtils {
public static void main(String[] args) throws Exception {
// 保存jvm運(yùn)行過(guò)程中生成的lambda字節(jié)碼文件到指定路徑
System.getProperties().put("jdk.internal.lambda.dumpProxyClasses", "F:/lambda");
String getName = BeanUtils.convertToFieldName(Person::getId);
String setName = BeanUtils.convertToFieldName(Person::setName);
System.out.println(getName);
System.out.println(setName);
}
/**
* 通過(guò)getter的方法引用獲取字段名
*/
public static <T> String convertToFieldName(IGetter<T> fn) throws Exception {
SerializedLambda lambda = getSerializedLambda(fn);
String methodName = lambda.getImplMethodName();
String prefix = null;
if (methodName.startsWith("get")) {
prefix = "get";
} else if (methodName.startsWith("is")) {
prefix = "is";
}
if (prefix == null) {
System.out.println("無(wú)效的getter方法: " + methodName);
}
return toLowerCaseFirstOne(methodName.replace(prefix, ""));
}
/**
* 通過(guò)setter的方法引用獲取字段名
*/
public static <T, U> String convertToFieldName(ISetter<T, U> fn) throws Exception {
SerializedLambda lambda = getSerializedLambda(fn);
String methodName = lambda.getImplMethodName();
if (!methodName.startsWith("set")) {
System.out.println("無(wú)效的setter方法:" + methodName);
}
return toLowerCaseFirstOne(methodName.replace("set", ""));
}
/**
* 關(guān)鍵在于這個(gè)方法
*/
public static SerializedLambda getSerializedLambda(Serializable fn) throws Exception {
Method method = fn.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(Boolean.TRUE);
SerializedLambda lambda = (SerializedLambda) method.invoke(fn);
return lambda;
}
/**
* 字符串首字母轉(zhuǎn)小寫
*/
public static String toLowerCaseFirstOne(String field) {
if (Character.isLowerCase(field.charAt(0)))
return field;
else {
char firstOne = Character.toLowerCase(field.charAt(0));
String other = field.substring(1);
return new StringBuilder().append(firstOne).append(other).toString();
}
}
}
運(yùn)行結(jié)果
id
name
其中最難理解的應(yīng)該是下面這句代碼,writeReplace方法是什么
Method method = fn.getClass().getDeclaredMethod("writeReplace");
接下來(lái)我們通過(guò)jvm運(yùn)行時(shí)保存的lambda字節(jié)碼來(lái)一探究竟
首先運(yùn)行完成后會(huì)生成方法引用的字節(jié)碼文件

1223.png
反編譯以后
// Person::getId
package lambda;
import java.lang.invoke.SerializedLambda;
final class BeanUtils$$Lambda$1 implements IGetter {
@Hidden
public Object get(Object obj) {
return Integer.valueOf(((Person) obj).getId());
}
private final Object writeReplace() {
return new SerializedLambda(lambda/BeanUtils, "lambda/IGetter", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", 5, "lambda/Person", "getId", "()I", "(Llambda/Person;)Ljava/lang/Object;", new Object[0]);
}
private BeanUtils$$Lambda$1() {
}
}
// Person::setName
package lambda;
import java.lang.invoke.SerializedLambda;
final class BeanUtils$$Lambda$2 implements ISetter {
@Hidden
public void set(Object obj, Object obj1) {
((Person) obj).setName((String) obj1);
}
private final Object writeReplace() {
return new SerializedLambda(lambda / BeanUtils, "lambda/ISetter", "set", "(Ljava/lang/Object;Ljava/lang/Object;)V", 5, "lambda/Person", "setName", "(Ljava/lang/String;)V", "(Llambda/Person;Ljava/lang/String;)V", new Object[0]);
}
private BeanUtils$$Lambda$2() {
}
}
原理一目了然
總結(jié)
- 要得到
Lambda表達(dá)式中方法引用的方法名,目前已知的方式是通過(guò)SerializedLambda; -
SerializedLambda是對(duì)Lambda表達(dá)式進(jìn)行描述的對(duì)象,在Lambda表達(dá)式可序列化的時(shí)候(函數(shù)式接口繼承Serializable)才能得到; - 函數(shù)式接口繼承
Serializable時(shí),編譯器在編譯Lambda表達(dá)式時(shí),生成了一個(gè)writeReplace方法,這個(gè)方法會(huì)返回SerializedLambda,可以反射調(diào)用這個(gè)方法;