Операция Java SAO - используйте лямбда вместо строки

Java задняя часть MyBatis

Получить подробную информацию о лямбда

Сказать, что?

В Java8 добавлены лямбда-выражения, чаще всего они используются для сотрудничестваStreamДелайте операции по сбору. Вот вещь, похожая на пасхальное яйцо, которую можно чудесно использовать для определенных случаев.

Общее использование, например следующее

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

или упрощенно до этого

Optional.of(1L).ifPresent(System.out::println);

Есть ли способ получитьSystem.out::printlnСтрока имени метода внутриString methodName = "println"?

Какой эффект?

выполнить код

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

выход

方法名:bar

Как это сделать?

Шаг 1: ОпределитеFunctionalInterface(Постучать по доске, нарисовать ключевые моментыextends Serializable)

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

Второе полотно: приготовить класс (соевый соус)

import lombok.Data;

/**
 * @author liuyuyu
 */
@Data
public class Foo {
    private Integer bar;
}

Шаг 3: ПолучитеFnинформационные инструменты

  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);

Шаг 4: Напишите грушу и бегите

/**
 * @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. Любой, кто реализует этот интерфейс (интерфейс является наследованием, также считается), Java должен предоставить методы сериализации и десериализации (ObjectInputStream/ObjectOutputStreamможет что-то напомнить).

Но лямбда особенная, это метод, ее можно рассматривать как действие (или кунг-фу? Например, Jiuyin Zhenjing), ее нельзя сохранить напрямую, Java предоставляетSerializedLambdaЭтот класс содержит информацию о лямбде.

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);

Таким образом, мы можем получить имя метода лямбды

Что еще можно сделать?

В дизайне фреймворка во многих сценариях необходимо получить атрибуты класса.До Java 8 дизайн API мог использовать только строковый метод.Если это Java 8, строки можно избежать.

Вы можете открыть каштан (лжеца) и запустить код напрямую, чтобы почувствовать его.

Расширение универсального картографа Mybatis

Получить демонстрацию метаинформации лямбда