通过lombok @Getter注解可以减少代码中对象属性getter方法的冗余,而lombok在编译时期会对增加@Getter注解的属性添加相应的getter方法,其对@Getter处理实现类为HandlerGetter.java,如下是使用@Getter后的几个注意事项。
1.lombok @Getter会优先使用对象类中存在的getter()方法。
for (String altName : toAllGetterNames(fieldNode)) {
switch (methodExists(altName, fieldNode, false , 0 )) {
case EXISTS_BY_LOMBOK:
return ;
case EXISTS_BY_USER:
if (whineIfExists) {
String altNameExpl = "" ;
if (!altName.equals(methodName)) altNameExpl = String.format( " (%s)" , altName);
source.addWarning(
String.format( "Not generating %s(): A method with that name already exists%s" , methodName, altNameExpl));
}
return ;
default :
case NOT_EXISTS:
//continue scanning the other alt names.
}
}
long access = toJavacModifier(level) | (fieldDecl.mods.flags & Flags.STATIC);
injectMethod(fieldNode.up(), createGetter(access, fieldNode, fieldNode.getTreeMaker(), source.get(), lazy, onMethod), List.<Type>nil(), getMirrorForFieldType(fieldNode));
|
从上面可以看到如果用户提供了getter方法,即case EXISTS_BY_USER,则不再为此field创建新的getter方法。
2.布尔类型(boolean 注意boolean 不等同Boolean)is开头生成Getter方法
private static String toAccessorName(AST<?, ?, ?> ast, AnnotationValues<Accessors> accessors, CharSequence fieldName, boolean isBoolean,
String booleanPrefix, String normalPrefix, boolean adhereToFluent) {
fieldName = fieldName.toString();
if (fieldName.length() == 0 ) return null ;
if (Boolean.TRUE.equals(ast.readConfiguration(ConfigurationKeys.GETTER_CONSEQUENT_BOOLEAN))) isBoolean = false ;
boolean explicitPrefix = accessors != null && accessors.isExplicit( "prefix" );
boolean explicitFluent = accessors != null && accessors.isExplicit( "fluent" );
Accessors ac = (explicitPrefix || explicitFluent) ? accessors.getInstance() : null ;
List<String> prefix = explicitPrefix ? Arrays.asList(ac.prefix()) : ast.readConfiguration(ConfigurationKeys.ACCESSORS_PREFIX);
boolean fluent = explicitFluent ? ac.fluent() : Boolean.TRUE.equals(ast.readConfiguration(ConfigurationKeys.ACCESSORS_FLUENT));
fieldName = removePrefix(fieldName, prefix);
if (fieldName == null ) return null ;
String fName = fieldName.toString();
if (adhereToFluent && fluent) return fName;
if (isBoolean && fName.startsWith( "is" ) && fieldName.length() > 2 && !Character.isLowerCase(fieldName.charAt( 2 ))) {
// The field is for example named 'isRunning'.
return booleanPrefix + fName.substring( 2 );
}
return buildAccessorName(isBoolean ? booleanPrefix : normalPrefix, fName);
}
|
以上suffix为字段名,booleanPrefix为is,可以看到是布尔类型的字段,且以is开头,第三个字母小写则生成的方法为isXXX(),否则走注意事项三, 如果一个布尔类型的字段如isrunning执行事项三,则此时其prefix 为is,则生成的方法为isIsrunning。
3.非布尔is开头字段生成Getter方法,则会把首字母变成大写。
public static String buildAccessorName(String prefix, String suffix) {
if (suffix.length() == 0 ) return prefix;
if (prefix.length() == 0 ) return suffix;
char first = suffix.charAt( 0 );
if (Character.isLowerCase(first)) {
boolean useUpperCase = suffix.length() > 2 &&
(Character.isTitleCase(suffix.charAt( 1 )) || Character.isUpperCase(suffix.charAt( 1 )));
suffix = String.format( "%s%s" ,
useUpperCase ? Character.toUpperCase(first) : Character.toTitleCase(first),
suffix.subSequence( 1 , suffix.length()));
}
return String.format( "%s%s" , prefix, suffix);
}
|
以上suffix为字段名,prefix为get,可以看到如果field名称首字母大写,例如Axxx那么生成的getter方法为getAxxx(), 如果首字母小写可以看到其把首字母变成了大写。
根据以上所述我们可以看下通过lombok生成的getter方法的一个类
源文件:
@Getter
private static class LombokGetter{
private boolean isRunning; // 对应注意事项2
private boolean isrunning; //对应注意事项2,3
private Boolean isStop; //对应注意事项3
private String yAxises; //对应注意事项3
}
编译后文件
private static class LombokGetter
{
private boolean isRunning;
private boolean isrunning;
private Boolean isStop;
private String yAxises;
public boolean isRunning()
{
return this .isRunning;
}
public boolean isIsrunning()
{
return this .isrunning;
}
public Boolean getIsStop()
{
return this .isStop;
}
public String getYAxises()
{
return this .yAxises;
}
}
|
下面我们看下在开发中我们遇到的几个问题
public static void main(String[] args) throws Exception{
System.out.println( new ObjectMapper().writeValueAsString( new LombokGetter()));
}
@Getter
private static class LombokGetter{
private boolean isRunning; // 对应注意事项2
private boolean isrunning; //对应注意事项2,3
private Boolean isStop; //对应注意事项2,3
private String yAxises; //对应注意事项3
}
print:
{ "isrunning" : false , "isStop" : null , "running" : false , "yaxises" : null }
|
针对yaxises我们可能更想得到yAxisses,但是经过jackon处理后得到的是yaxises,在编译文件中我们可以看到其get方法为getYAxises();
经过jackson处理后得到的是yaxises
protected static String legacyManglePropertyName( final String basename, final int offset)
{
final int end = basename.length();
if (end == offset) { // empty name, nope
return null ;
}
// next check: is the first character upper case? If not, return as is
char c = basename.charAt(offset);
char d = Character.toLowerCase(c);
if (c == d) {
return basename.substring(offset);
}
// otherwise, lower case initial chars. Common case first, just one char
StringBuilder sb = new StringBuilder(end - offset);
sb.append(d);
int i = offset+ 1 ;
for (; i < end; ++i) {
c = basename.charAt(i);
d = Character.toLowerCase(c);
if (c == d) {
sb.append(basename, i, end);
break ;
}
sb.append(d);
}
return sb.toString();
}
|
代码中offset为3,basename为getYAxises,可以看到最后生成的name 为yaxises,其是从第四个字符开始转化成小写字符,直到遇到小写字母终止,jackson默认采用上面的方式生成name,当然还可以配置其他方式,将在jackson篇幅进行介绍。
因此综上所述个人认为lombok对注意事项二,注意事项三的处理并不是lombok的bug,只不过其规约没有和jackson的序列化规约一致。