layout: post
title: Java8使用Lamda代替字符串
categories: [Java]
description: Java8新特性
keywords: Java
效果
public class User {
private String name;
public String getName() {
return name;
}
}
public static void main(String[] args) {
String name = LambdaUtils.convertToFieldName(User::getName);
System.out.println(name); //name
}
原理
SerializedLambda
是jdk1.8提供的一个新的类,凡是继承了Serializable的函数式接口的实例都可以获取一个属于它的SerializedLambda实例,并且通过它获取到方法的名称,根据我们标准的java bean的定义规则就可以通过方法名称来获取属性名称。
实现
从lambda字节码中获取writeReplace
方法,反射调用返回lambda的SerializedLambda实例,再从SerializedLambda实例中获取方法名,然后将方法名转换成属性名
工具类
/**
* @author niuyy
* @since 2020/3/20
*/
public class LambdaUtils {
/**
* 方法缓存
*/
private static Map<Class, SerializedLambda> CLASS_LAMBDA_CACHE = new ConcurrentHashMap<>();
/**
* 从lambda表达式获取SerializedLambda实例
*
* @param fn lambda表达式
* @return 获取SerializedLambda
*/
public static SerializedLambda getSerializedLambda(Serializable fn) {
SerializedLambda lambda = CLASS_LAMBDA_CACHE.get(fn.getClass());
if (lambda == null) {
try {
Method method = fn.getClass().getDeclaredMethod("writeReplace");
method.setAccessible(Boolean.TRUE);
lambda = (SerializedLambda) method.invoke(fn);
CLASS_LAMBDA_CACHE.put(fn.getClass(), lambda);
} catch (Exception e) {
e.printStackTrace();
}
}
return lambda;
}
/***
* 转换方法引用为属性名
*
* @param fn 方法引用
* @return 属性名
*/
public static <T, R> String convertToFieldName(SFunction<T, R> fn) {
SerializedLambda lambda = getSerializedLambda(fn);
String methodName = lambda.getImplMethodName();
return methodToProperty(methodName);
}
/**
* 方法名转换成属性名
*
* @param name 方法名
* @return 属性名
*/
public static String methodToProperty(String name) {
if (name.startsWith("is")) {
name = name.substring(2);
} else if (name.startsWith("get") || name.startsWith("set")) {
name = name.substring(3);
} else {
throw new RuntimeException("Error parsing property name '" + name + "'. Didn't start with 'is', 'get' or 'set'.");
}
if (name.length() == 1 || (name.length() > 1 && !Character.isUpperCase(name.charAt(1)))) {
name = name.substring(0, 1).toLowerCase(Locale.ENGLISH) + name.substring(1);
}
return name;
}
}
总结
从SerializedLambda中还可以获取其他信息:
其中,implMethodKind 参考 MethodHandleInfo 类
static final byte
REF_NONE = 0, // null value
REF_getField = 1,
REF_getStatic = 2,
REF_putField = 3,
REF_putStatic = 4,
REF_invokeVirtual = 5,
REF_invokeStatic = 6,
REF_invokeSpecial = 7,
REF_newInvokeSpecial = 8,
REF_invokeInterface = 9,
REF_LIMIT = 10;
MORE
对于这样的 lambda
String name1 = LambdaUtils.convertToFieldName((User u,String name2) -> {
String name3 = u.getName();
u.setName(name3 + 111);
});
SerializedLambda 信息如下