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,就能夠避免字符串。
你們能夠打開(騙星)栗子,直接運行代碼感覺一下。