知其然知其所以然:Boolean类型命名规范


对应阿里巴巴开发手册-编码规约-命名风格第9条,规约中写的比较模糊,之前只知道这样命名可能会存在问题,这里探索一下具体什么场景会存在问题。

对上面举的反例感觉有些怪,**基本数据类型Boolean isDeleted属性的Getter方法是isDeleted()**,手册应该写的有问题,印象中基本数据类型boolean是这样的,但是Boolean包装类型并不是如此,后面会对这里进行探索,坑很大。

实例深究

JavaBean规范

首先了解下JavaBean的规范,属性应该由一组读写方法(Getter/Setter)来访问,如果操作的字段为boolean类型,此时Getter不应命名为getXXX方法,而应该是isXXX,如果boolean类型的属性名以is开头,则将属性名中的is自动去除,例如boolean isSuccess的Getter是isSuccess(),但是对于Boolean类型的属性并没有明确的规范进行约定。

以下示例基于IDEA 2023.1 JDK11

布尔类型存在基本数据类型和包装类型,所以存在四种情况

private boolean success;
private boolean isSuccess;
private Boolean success;
private Boolean isSuccess;

通过IDEA自动生成Getter/Setter,可以得到

class Result1{
    private boolean success;

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }
}


class Result2{
    private boolean isSuccess;

    public boolean isSuccess() {
        return isSuccess;
    }

    public void setSuccess(boolean success) {
        isSuccess = success;
    }
}

class Result3{
    private Boolean success;

    public Boolean getSuccess() {
        return success;
    }

    public void setSuccess(Boolean success) {
        this.success = success;
    }
}

class Result4{
    private Boolean isSuccess;

    public Boolean getSuccess() {
        return isSuccess;
    }

    public void setSuccess(Boolean success) {
        isSuccess = success;
    }
}

从IDEA自动生成的Getter/Setter可以看出

  • boolean类型Getter是isXXX()形式,如果以is开头则会省略is
  • Boolean类型Getter是getXXX()形式,如果以is开头则会省略is
  • 两种类型的Setter是相同的

序列化

开发手册的规约中提到,is开头的属性在部分框架解析时会引起序列化的错误,那么拿常用的JSON序列化进行举例,分别使用JackJson、FastJson和Gson进行序列化以及反序列化来验证

Result1 result1 = new Result1();
result1.setSuccess(true);

Result2 result2 = new Result2();
result2.setSuccess(true);

Result3 result3 = new Result3();
result3.setSuccess(true);

Result4 result4 = new Result4();
result4.setSuccess(true);

ObjectMapper jackson = new ObjectMapper();
Gson gson = new Gson();

System.out.println("boolean success");
System.out.println("jackson: " + jackson.writeValueAsString(result1));
System.out.println("gson: " + gson.toJson(result1));
System.out.println("fastjson: " + JSON.toJSONString(result1));


System.out.println("boolean isSuccess");
System.out.println("jackson: " + jackson.writeValueAsString(result2));
System.out.println("gson: " + gson.toJson(result2));
System.out.println("fastjson: " + JSON.toJSONString(result2));

System.out.println("Boolean success");
System.out.println("jackson: " + jackson.writeValueAsString(result3));
System.out.println("gson: " + gson.toJson(result3));
System.out.println("fastjson: " + JSON.toJSONString(result3));


System.out.println("Boolean isSuccess");
System.out.println("jackson: " + jackson.writeValueAsString(result4));
System.out.println("gson: " + gson.toJson(result4));
System.out.println("fastjson: " + JSON.toJSONString(result4));

运行结果:

boolean success
jackson: {"success":true}
gson: {"success":true}
fastjson: {"success":true}

boolean isSuccess
jackson: {"success":true}
gson: {"isSuccess":true}
fastjson: {"success":true}

Boolean success
jackson: {"success":true}
gson: {"success":true}
fastjson: {"success":true}

Boolean isSuccess
jackson: {"success":true}
gson: {"isSuccess":true}
fastjson: {"success":true}

根据这些结果以及对应JSON框架的分析可以得出:

  • jackson和fastjson在进行对象的序列化时,是通过反射遍历出该类中所有的Getter方法,得到isSuccess(),他们会认为字段属性是success,然后序列化成json
  • Gson是通过反射遍历类中所有的属性,并将其值序列化成json

由于不同的序列化工具,在进行序列化时使用的策略并不一致,所以对于同一个对象序列化的结果可能并不一致。

反序列化

那么使用相同的json反序列时会发生什么情况呢?

{"isSuccess":true}
String jsonString = "{\"isSuccess\":true}";

ObjectMapper jackson = new ObjectMapper();
Gson gson = new Gson();

System.out.println("boolean success");
try{
    Result1 result1 = jackson.readValue(jsonString, Result1.class);
    System.out.println("jackson: " + result1);
}catch (Exception e){
    System.out.println("jackson反序列化异常" + e.getMessage());
}

try{
    Result2 result2 = gson.fromJson(jsonString, Result2.class);
    System.out.println("gson: " + result2);
}catch (Exception e){
    System.out.println("gson反序列化异常" + e.getMessage());

}

try{
    Result3 result3 = JSON.parseObject(jsonString, Result3.class);
    System.out.println("fastjson: " + result3);
}catch (Exception e){
    System.out.println("fastjson反序列化异常" + e.getMessage());
}

执行结果:

jackson反序列化异常Unrecognized field "isSuccess" (class com.meifute.m2.item.BooleanNameTest$Result1), not marked as ignorable (one known property: "success"])
 at [Source: (String)"{"isSuccess":true}"; line: 1, column: 18] (through reference chain: com.meifute.m2.item.BooleanNameTest$Result1["isSuccess"])
gson: Result2{isSuccess=true}
fastjson: Result3{success=true}

通过以上执行结果可以看出,jackson反序列化时异常,那么在实际项目中,我们经常会将我们的json序列化工具设置为如果没有字段自动忽略,不要抛出异常的形式,那么再将测试代码执行测试

String jsonString = "{\"isSuccess\":true}";

ObjectMapper jackson = new ObjectMapper();
jackson.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

Gson gson = new Gson();

System.out.println("boolean success");
try{
    Result1 result1 = jackson.readValue(jsonString, Result1.class);

    System.out.println("jackson: " + result1);
}catch (Exception e){
    System.out.println("jackson反序列化异常" + e.getMessage());
}

try{
    Result2 result2 = gson.fromJson(jsonString, Result2.class);
    System.out.println("gson: " + result2);
}catch (Exception e){
    System.out.println("gson反序列化异常" + e.getMessage());

}

try{
    Result3 result3 = JSON.parseObject(jsonString, Result3.class);
    System.out.println("fastjson: " + result3);
}catch (Exception e){
    System.out.println("fastjson反序列化异常" + e.getMessage());
}

执行结果:

boolean success
jackson: Result1{success=false}
gson: Result2{isSuccess=true}
fastjson: Result3{success=true}

可以看出,jackson在反序列化时,如果忽略不匹配的字段,boolean会存在默认值false,导致预期和匹配不符。

那么其实反序列化的这个场景,和阿里巴巴开发手册中中这条正是交响呼应(但是这条规约也是有待商榷,Boolean存在null,对于仅是否两种情况下,业务中不处理可能会存在NPE的问题)

通过上面的例子,可以看到布尔类型为什么不要使用is开头的原因,是因为各种序列化工具的策略不一样,稍有不慎有可能导致预期和结果不符的情况出现。

Getter深究

JavaBean规范对boolean类型的Getter进行了明确的约定,但是Boolean并没有明确的约定,IDEA对于is开头的Boolean属性会自动去掉is并生成getXXX样式的Getter,那么所有的开发工具或者代码生成工具都是如此处理的吗?

带着这个疑问,在eclipse中进行了试用,eclispe中对于boolean属性Getter的生成和IDEA一样是符合JavaBean规范的,但是对于is开头的Boolean属性生成的Getter是getIsXXX样式,和IDEA中不一样。

那么像经常使用lambok中又是如何处理的呢?经过测试lambok的处理方式和eclipse一致。

也就是说由于Boolean并没有明确的约定,在具体的项目和团队中,可以根据自己的编码规范和偏好进行调整和约定,那么再结合序列化工具,又有可能带来未知的问题。

为了避免使用不当可能引发的问题,干脆规约中进行字段命名的限制,也是一定的道理。


文章作者: gloamfox
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 gloamfox !
 上一篇
Spring事件监听在业务使用中的优化
本文深入探讨了Spring框架中事件驱动机制的使用与优化,特别是在电商订单创建场景下的应用。文章详细分析了事件定义、发布和监听的实现方式,并针对异步执行中遇到的数据一致性、持久化和线程变量传递等问题提出了创新解决方案。通过自定义注解和切面增强,实现了对Spring事件监听功能的扩展,提供了更可靠的异步事件处理机制。
2025-12-18
下一篇 
Ubuntu 安装 Docker 操作手册
本文档详细介绍了在 Ubuntu 系统上安装 Docker 引擎的步骤,包括系统要求、卸载旧版本、使用 apt 仓库安装、配置国内镜像源、验证安装以及后续配置等。
2025-12-06
  目录