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的方法名