2020-03-19-Java8使用Lamda代替字符串


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_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  String convertToFieldName(SFunction 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中还可以获取其他信息:

image-20200323191947102.png

其中,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 信息如下

image-20200323192352796.png

你可能感兴趣的:(2020-03-19-Java8使用Lamda代替字符串)