lambda 方法引用獲取字段屬性以及實(shí)現(xiàn)原理

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è)方法;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容