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

获取lambda的详细信息

要说啥?

Java8新增了lambda表达式,最常见的用法是配合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

怎么做?

第一步:定义一个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;
}
复制代码

第三步:获取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);

第四步:写个梨子跑起来

/**
 * @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

转载于:https://juejin.im/post/5b0948d06fb9a07aa6323866

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值