【java】获取属性名工具

背景

在编码时经常会用到同名的属性名字符串,比如

  1. 用相同的属性名做为 map 中的键;
  2. 在 mybatis 中,根据属性名的下划线字符串来拼接 sql 查询条件。

需要修改属性名时,如果是用字符串硬编码的,引用的地方越多,修改越困难

但是如果用的是 java8 中的属性引用,操作起来就很方便了,修改一处即可修改全部相关引用。

属性工具类测试

参考下面测试类,怎样使用;

如果想要修改 articleNamearticleTitle

在 IDEA 中,修改类的属性名很方便,选中属性名 articleName,按下快捷键 <Shift + F6>,键入新的属性名称 articleTitle,确认即可替换所有关联的属性名称


import lombok.Getter;
import lombok.Setter;

/**
 * 属性工具类测试
 *
 * @author lilou
 */
public class FieldUtilTest {

    @Setter
    @Getter
    static class Article {
        String articleName;
        String articleContent;
    }

    public static void main(String[] args) {

        // test getter
        System.out.println(FieldUtil.noPrefix(Article::getArticleName));
        System.out.println(FieldUtil.underline(Article::getArticleName));
        System.out.println(FieldUtil.underlineUpper(Article::getArticleContent));
        System.out.println(FieldUtil.toSymbolCase(Article::getArticleName, '$'));


        // test setter
        System.out.println(FieldUtil.noPrefix(Article::setArticleName));
        System.out.println(FieldUtil.underline(Article::setArticleName));
        System.out.println(FieldUtil.underlineUpper(Article::setArticleContent));
        System.out.println(FieldUtil.toSymbolCase(Article::setArticleName, '$'));
    }
}

属性工具类代码

关键逻辑是利用了 java8 中的 SerializedLambda 的 getImplMethodName 方法来获取属性名。

源码中引用了 hutool 第三方工具类的 StrUtil工具,方便操作字符串,当然也可自行开发。

参考资料:利用 Lambda 实现通过 getter/setter 方法引用拿到属性名 - SegmentFault 思否


import cn.hutool.core.util.StrUtil;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;

/**
 * 属性工具类,用来获取 Getter 和 Setter 属性的名称。支持首字母小写样式,下划线的样式和自定义样式
 * <p>
 * 参考:[利用Lambda实现通过getter/setter方法引用拿到属性名 - SegmentFault 思否](https://segmentfault.com/a/1190000019389160)
 *
 * @author lilou
 */
public class FieldUtil {

    /*
     * ===========> getter 方法引用 <===========
     */

    /**
     * 下划线样式,小写
     */
    public static <T> String underline(IGetter<T> fn) {
        return toSymbolCase(fn, '_');
    }

    /**
     * 下划线样式,大写
     */
    public static <T> String underlineUpper(IGetter<T> fn) {
        return underline(fn).toUpperCase();
    }

    /**
     * 依据符号转换样式
     */
    public static <T> String toSymbolCase(IGetter<T> fn, char symbol) {
        return StrUtil.toSymbolCase(noPrefix(fn), symbol);
    }

    /***
     * 转换getter方法引用为属性名,首字母小写
     */
    public static <T> String noPrefix(IGetter<T> fn) {
        return getGeneralField(fn);
    }

    /*
     * ===========> setter 方法引用 <===========
     */

    /**
     * 下划线样式,小写
     */
    public static <T, R> String underline(ISetter<T, R> fn) {
        return toSymbolCase(fn, '_');
    }

    /**
     * 下划线样式,大写
     */
    public static <T, R> String underlineUpper(ISetter<T, R> fn) {
        return underline(fn).toUpperCase();
    }

    /**
     * 依据符号转换样式
     */
    public static <T, R> String toSymbolCase(ISetter<T, R> fn, char symbol) {
        return StrUtil.toSymbolCase(noPrefix(fn), symbol);
    }

    /**
     * 转换setter方法引用为属性名,首字母小写
     */
    public static <T, R> String noPrefix(ISetter<T, R> fn) {
        return getGeneralField(fn);
    }


    /*
     * ===========> 复用功能 <===========
     */

    /**
     * 获得set或get或is方法对应的标准属性名,其它前缀的方法名使用原名
     */
    private static String getGeneralField(Serializable fn) {
        SerializedLambda lambda = getSerializedLambda(fn);
        String getOrSetMethodName = lambda.getImplMethodName();
        final String generalField = StrUtil.getGeneralField(getOrSetMethodName);
        return StrUtil.isEmpty(generalField) ? getOrSetMethodName : generalField;
    }

    /***
     * 获取类对应的Lambda
     */
    private static SerializedLambda getSerializedLambda(Serializable fn) {
        //先检查缓存中是否已存在
        SerializedLambda lambda;
        try {

            //提取SerializedLambda并缓存
            Method method = fn.getClass().getDeclaredMethod("writeReplace");
            method.setAccessible(Boolean.TRUE);
            lambda = (SerializedLambda) method.invoke(fn);
        } catch (Exception e) {
            throw new IllegalArgumentException("获取SerializedLambda异常, class=" + fn.getClass().getSimpleName(), e);
        }
        return lambda;
    }

    /**
     * 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);
    }
}
posted @ 2022-01-13 11:37  lyloou  阅读(609)  评论(0编辑  收藏  举报