Java SAO操做-使用lambda代替字符串

獲取lambda的詳細信息

要說啥?

Java8新增了lambda表達式,最多見的用法是配合Stream作集合操做。下面是一種相似彩蛋的東西能夠妙用到某些場合。java

通常用法,好比下面這樣git

Optional.of(1L).ifPresent(number -> {
    System.out.println(number);
});
複製代碼

或者簡化成這樣github

Optional.of(1L).ifPresent(System.out::println);
複製代碼

有什麼辦法能獲取到System.out::println裏面的方法名字符串String methodName = "println"api

啥效果?

執行代碼bash

FnConverter<Foo> fnConverter = new FnConverter<>();
String fieldName = fnConverter.convertFnToString(Foo::getBar);
System.out.println("方法名:"+fieldName);
複製代碼

輸出app

方法名:bar框架

怎麼作?

第一步:定義一個FunctionalInterface(敲黑板,畫重點extends Serializable工具

/**
 * @author Frank
 */
@FunctionalInterface
public interface Fn<T> extends Serializable {
    Object apply(T source);
}
複製代碼

第二布:準備個類(醬油)ui

import lombok.Data;

/**
 * @author liuyuyu
 */
@Data
public class Foo {
    private Integer bar;
}
複製代碼

第三步:獲取Fn的信息的工具類spa

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加強

lambda meta信息獲取demo

相關文章
相關標籤/搜索