乐趣区

利用Lambda实现通过gettersetter方法引用拿到属性名

有很多开发场景需要用到 Java Bean 的属性名,直接写死属性名字符串的形式容易产生 bug(属性名一旦变化,IDE 不会告诉你你的字符串需要同步修改)。JDK8 的 Lambda 可以通过方法引用简化代码,同样也可以通过 getter/setter 的方法引用拿到属性名,避免潜在的 bug。

1. 定义 FunctionalInterface 接收方法引用

/**
 * getter 方法接口定义
 */
@FunctionalInterface
public interface IGetter<T> extends Serializable {Object apply(T source);
}
/**
 * setter 方法接口定义
 */
@FunctionalInterface
public interface ISetter<T, U> extends Serializable {void accept(T t, U u);
}

2. 定义 getter/setter 引用转换属性名的工具类

public class BeanUtils {
    ...
    /**
     * 缓存类 -Lambda 的映射关系
     */
    private static Map<Class, SerializedLambda> CLASS_LAMDBA_CACHE = new ConcurrentHashMap<>();

    /***
     * 转换方法引用为属性名
     * @param fn
     * @return
     */
    public static <T> String convertToFieldName(IGetter<T> fn) {SerializedLambda lambda = getSerializedLambda(fn);
        String methodName = lambda.getImplMethodName();
        String prefix = null;
        if(methodName.startsWith("get")){prefix = "get";}
        else if(methodName.startsWith("is")){prefix = "is";}
        if(prefix == null){log.warn("无效的 getter 方法:"+methodName);
        }
        // 截取 get/is 之后的字符串并转换首字母为小写(S 为 diboot 项目的字符串工具类,可自行实现)return S.uncapFirst(S.substringAfter(methodName, prefix));
    }
    
    /***
     * 转换 setter 方法引用为属性名
     * @param fn
     * @return
     */
    public static <T,R> String convertToFieldName(ISetter<T,R> fn) {SerializedLambda lambda = getSerializedLambda(fn);
        String methodName = lambda.getImplMethodName();
        if(!methodName.startsWith("set")){log.warn("无效的 setter 方法:"+methodName);
        }
        // 截取 set 之后的字符串并转换首字母为小写(S 为 diboot 项目的字符串工具类,可自行实现)return S.uncapFirst(S.substringAfter(methodName, "set"));
    }
    
    /***
     * 获取类对应的 Lambda
     * @param fn
     * @return
     */
    private static SerializedLambda getSerializedLambda(Serializable fn){
        // 先检查缓存中是否已存在
        SerializedLambda lambda = CLASS_LAMDBA_CACHE.get(fn.getClass());
        if(lambda == null){
            try{// 提取 SerializedLambda 并缓存
                Method method = fn.getClass().getDeclaredMethod("writeReplace");
                method.setAccessible(Boolean.TRUE);
                lambda = (SerializedLambda) method.invoke(fn);
                CLASS_LAMDBA_CACHE.put(fn.getClass(), lambda);
            }
            catch (Exception e){log.error("获取 SerializedLambda 异常, class="+fn.getClass().getSimpleName(), e);
            }
        }
        return lambda;
    }
}

3. 开心的引用

// 方法引用替代 hard code 字符串,属性名变化时 IDE 会同步提示
String ITEM_NAME = BeanUtils.convertToFieldName(User::getOrgName);
// 等同于 String ITEM_NAME = "orgName";

Diboot – 简单高效的轻代码开发框架

退出移动版