【Java】获取属性名工具

背景

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

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

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

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

属性工具类测试

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

如果想要修改 articleNamearticleTitle

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

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 思否

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129

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);
}
}