【java】获取属性名工具
背景
在编码时经常会用到同名的属性名字符串,比如
- 用相同的属性名做为 map 中的键;
- 在 mybatis 中,根据属性名的下划线字符串来拼接 sql 查询条件。
需要修改属性名时,如果是用字符串硬编码的,引用的地方越多,修改越困难
但是如果用的是 java8 中的属性引用,操作起来就很方便了,修改一处即可修改全部相关引用。
属性工具类测试
参考下面测试类,怎样使用;
如果想要修改 articleName
为 articleTitle
,
在 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);
}
}