为什么阿里巴巴禁止开发人员使用isSuccess作为变量名

在日常开发中,我们会经常要在类中定义布尔类型的变量,比如在给外部系统提供一个RPC接口的时候,我们一般会定义一个字段表示本次请求是否成功的。

关于这个”本次请求是否成功”的字段的定义,其实是有很多种讲究和坑的,稍有不慎就会掉入坑里,作者在很久之前就遇到过类似的问题,本文就来围绕这个简单分析一下。到底该如何定一个布尔类型的成员变量。

一般情况下,我们可以有以下四种方式来定义一个布尔类型的成员变量:

boolean success
boolean isSuccess
Boolean success
Boolean isSuccess

以上四种定义形式,你日常开发中最常用的是哪种呢?到底哪一种才是正确的使用姿势呢?

通过观察我们可以发现,前两种和后两种的主要区别是变量的类型不同,前者使用的是boolean,后者使用的是Boolean。

另外,第一种和第三种在定义变量的时候,变量命名是success,而另外两种使用isSuccess来命名的。

首先,我们来分析一下,到底应该是用success来命名,还是使用isSuccess更好一点。

success 还是 isSucces

到底应该是用success还是isSuccess来给变量命名呢?从语义上面来讲,两种命名方式都可以讲的通,并且也都没有歧义。那么还有什么原则可以参考来让我们做选择呢。

在阿里巴巴Java开发手册中关于这一点,有过一个『强制性』规定:

那么,为什么会有这样的规定呢?我们看一下POJO中布尔类型变量不同的命名有什么区别吧。

class Model1  {
   private Boolean isSuccess;
   public void setSuccess(Boolean success) {
       isSuccess = success;
   }
   public Boolean getSuccess() {
       return isSuccess;
   }
}

class Model2 {
   private Boolean success;
   public Boolean getSuccess() {
       return success;
   }
   public void setSuccess(Boolean success) {
       this.success = success;
   }
}

class Model3 {
   private boolean isSuccess;
   public boolean isSuccess() {
       return isSuccess;
   }
   public void setSuccess(boolean success) {
       isSuccess = success;
   }
}

class Model4 {
   private boolean success;
   public boolean isSuccess() {
       return success;
   }
   public void setSuccess(boolean success) {
       this.success = success;
   }
}

仔细观察以上代码,你会发现以下规律:

  • 基本类型自动生成的getter和setter方法,名称都是isXXX()setXXX()形式的。
  • 包装类型自动生成的getter和setter方法,名称都是getXXX()setXXX()形式的。

既然,我们已经达成一致共识使用基本类型boolean来定义成员变量了,那么我们再来具体看下Model3和Model4中的setter/getter有何区别。

我们可以发现,虽然Model3和Model4中的成员变量的名称不同,一个是success,另外一个是isSuccess,但是他们自动生成的getter和setter方法名称都是isSuccesssetSuccess

Java Bean中关于setter/getter的规范

关于Java Bean中的getter/setter方法的定义其实是有明确的规定的,根据JavaBeans(TM) Specification规定,如果是普通的参数,命名为propertyName,需要通过以下方式定义其setter/getter:

public <PropertyType> get<PropertyName>();
public void set<PropertyName>(<PropertyType> a);

但是,布尔类型的变量propertyName则是另外一套命名原则的:

public boolean is<PropertyName>();
public void set<PropertyName>(boolean m);

通过对照这份JavaBeans规范,我们发现,在Model4中,变量名为isSuccess,如果严格按照规范定义的话,他的getter方法应该叫isIsSuccess。但是很多IDE都会默认生成为isSuccess。

那这样做会带来什么问题呢。

在一般情况下,其实是没有影响的。但是有一种特殊情况就会有问题,那就是发生序列化的时候。

序列化带来的影响

我们这里拿比较常用的JSON序列化来举例,看看常用的fastJson、jackson和Gson之间有何区别:

public class BooleanMainTest {

   public static void main(String[] args) throws IOException {
       //定一个Model3类型
       Model3 model3 = new Model3();
       model3.setSuccess(true);

       //使用fastjson(1.2.16)序列化model3成字符串并输出
       System.out.println("Serializable Result With fastjson :" + JSON.toJSONString(model3));

       //使用Gson(2.8.5)序列化model3成字符串并输出
       Gson gson =new Gson();
       System.out.println("Serializable Result With Gson :"+gson.toJson(model3));

       //使用jackson(2.9.7)序列化model3成字符串并输出
       ObjectMapper om = new ObjectMapper();
       System.out.println("Serializable Result With jackson :" +om.writeValueAsString(model3));
   }
}

class Model3 implements Serializable {

   private static final long serialVersionUID = 1836697963736227954L;
   private boolean isSuccess;
   public boolean isSuccess() {
       return isSuccess;
   }
   public void setSuccess(boolean success) {
       isSuccess = success;
   }
   public String getHollis(){
       return "hollischuang";
   }
}

以上代码的Model3中,只有一个成员变量即isSuccess,三个方法,分别是IDE帮我们自动生成的isSuccess和setSuccess,另外一个是作者自己增加的一个符合getter命名规范的方法。

以上代码输出结果:

Serializable Result With fastjson :{"hollis":"hollischuang","success":true}
Serializable Result With Gson :{"isSuccess":true}
Serializable Result With jackson :{"success":true,"hollis":"hollischuang"}

在fastjson和jackson的结果中,原来类中的isSuccess字段被序列化成success,并且其中还包含hollis值。而Gson中只有isSuccess字段。

我们可以得出结论:fastjson和jackson在把对象序列化成json字符串的时候,是通过反射遍历出该类中的所有getter方法,得到getHollis和isSuccess,然后根据JavaBeans规则,他会认为这是两个属性hollis和success的值。直接序列化成json:

{“hollis”:”hollischuang”,”success”:true}

但是Gson并不是这么做的,他是通过反射遍历该类中的所有属性,并把其值序列化成json:

{“isSuccess”:true}

可以看到,由于不同的序列化工具,在进行序列化的时候使用到的策略是不一样的,所以,对于同一个类的同一个对象的序列化结果可能是不同的。

前面提到的关于对getHollis的序列化只是为了说明fastjson、jackson和Gson之间的序列化策略的不同,我们暂且把他放到一边,我们把他从Model3中删除后,重新执行下以上代码,得到结果:

Serializable Result With fastjson :{"success":true}
Serializable Result WithGson :{"isSuccess":true}
Serializable Result With jackson :{"success":true}

现在,不同的序列化框架得到的json内容并不相同,如果对于同一个对象,我使用fastjson进行序列化,再使用Gson反序列化会发生什么?

public class BooleanMainTest {
   public static void main(String[] args) throws IOException {
       Model3 model3 = new Model3();
       model3.setSuccess(true);
       Gson gson =new Gson();
       System.out.println(gson.fromJson(JSON.toJSONString(model3),Model3.class));
   }
}

class Model3 implements Serializable {
   private static final long serialVersionUID = 1836697963736227954L;
   private boolean isSuccess;
   public boolean isSuccess() {
       return isSuccess;
   }
   public void setSuccess(boolean success) {
       isSuccess = success;
   }
   @Override
   public String toString() {
       return new StringJoiner(", ", Model3.class.getSimpleName() + "[","]")
           .add("isSuccess=" + isSuccess)
           .toString();
   }
}

以上代码,输出结果:

Model3[isSuccess=false]

这和我们预期的结果完全相反,原因是因为JSON框架通过扫描所有的getter后发现有一个isSuccess方法,然后根据JavaBeans的规范,解析出变量名为success,把model对象序列化城字符串后内容为{"success":true}

根据{"success":true}这个json串,Gson框架在通过解析后,通过反射寻找Model类中的success属性,但是Model类中只有isSuccess属性,所以,最终反序列化后的Model类的对象中,isSuccess则会使用默认值false。

但是,一旦以上代码发生在生产环境,这绝对是一个致命的问题。

所以,作为开发者,我们应该想办法尽量避免这种问题的发生,对于POJO的设计者来说,只需要做简单的一件事就可以解决这个问题了,那就是把isSuccess改为success。

这样,该类里面的成员变量时success,getter方法是isSuccess,这是完全符合JavaBeans规范的。无论哪种序列化框架,执行结果都一样。就从源头避免了这个问题。

引用一下R大关于阿里巴巴Java开发手册中这条规定的评价

(https://www.zhihu.com/question/55642203):

所以,在定义POJO中的布尔类型的变量时,不要使用isSuccess这种形式,而要直接使用success!

Boolean还是boolean?  

前面我们介绍完了在success和isSuccess之间如何选择,那么排除错误答案后,备选项还剩下:

boolean success
Boolean success

那么,到底应该是用Boolean还是boolean来给定一个布尔类型的变量呢?

我们知道,boolean是基本数据类型,而Boolean是包装类型。

那么,在定义一个成员变量的时候到底是使用包装类型更好还是使用基本数据类型呢?

我们来看一段简单的代码

public class BooleanMainTest {
   public static void main(String[] args) {
       Model model1 = new Model();
       System.out.println("default model : " + model1);
   }
}

class Model {
   /**
    * 定一个Boolean类型的success成员变量
    */
   private Boolean success;
   /**
    * 定一个boolean类型的failure成员变量
    */
   private boolean failure;

   /**
    * 覆盖toString方法,使用Java 8 的StringJoiner
    */
   @Override
   public String toString() {
       return new StringJoiner(", ", Model.class.getSimpleName() + "[","]")
           .add("success=" + success)
           .add("failure=" + failure)
           .toString();
   }
}

以上代码输出结果为:

default model : Model[success=null, failure=false]

可以看到,当我们没有设置Model对象的字段的值的时候,Boolean类型的变量会设置默认值为null,而boolean类型的变量会设置默认值为false

即对象的默认值是null,boolean基本数据类型的默认值是false

在阿里巴巴Java开发手册中,对于POJO中如何选择变量的类型也有着一些规定:

这里建议我们使用包装类型,原因是什么呢?

举一个扣费的例子,我们做一个扣费系统,扣费时需要从外部的定价系统中读取一个费率的值,我们预期该接口的返回值中会包含一个浮点型的费率字段。当我们取到这个值得时候就使用公式:金额*费率=费用 进行计算,计算结果进行划扣。

如果由于计费系统异常,他可能会返回个默认值,如果这个字段是Double类型的话,该默认值为null,如果该字段是double类型的话,该默认值为0.0。

如果扣费系统对于该费率返回值没做特殊处理的话,拿到null值进行计算会直接报错,阻断程序。拿到0.0可能就直接进行计算,得出接口为0后进行扣费了。这种异常情况就无法被感知。

这种使用包装类型定义变量的方式,通过异常来阻断程序,进而可以被识别到这种线上问题。如果使用基本数据类型的话,系统可能不会报错,进而认为无异常。

以上,就是建议在POJO和RPC的返回值中使用包装类型的原因。

在之前那篇文章的解析中,作者的观点是,对于布尔类型的变量,我认为可以和其他类型区分开来,作者并不认为使用null进而导致NPE是一种最好的实践。因为布尔类型只有true/false两种值,我们完全可以和外部调用方约定好当返回值为false时的明确语义。

本文来自微信公众号:Hollis

原文地址:https://www.cnblogs.com/Joker-Face/p/10176921.html

时间: 2024-11-01 04:28:40

为什么阿里巴巴禁止开发人员使用isSuccess作为变量名的相关文章

阿里巴巴Java开发手册正确学习姿势是怎样的?刷新代码规范认知

很多人都知道,阿里巴巴在2017发布了<阿里巴巴Java开发手册>,前后推出了很多个版本,并在后续推出了与之配套的IDEA插件和书籍. 相信很多Java开发都或多或少看过这份手册,这份手册有7个章节,覆盖了编程规约.异常日志.单元测试.安全规约.MySQL数据库.工程结构以及设计规约等方面. 这份规约可以说是覆盖了Java开发的方方面面,如果还有人没看的话,强烈建议大家好好看看,并且仔细研读. 手册中,有那么一些规则,是比较容易理解的.比如一些变量命名规范,有另外一些规则,是不太容易理解的,背

阿里巴巴Java开发手册1.4.0

转自官网 前言 <阿里巴巴Java开发手册>是阿里巴巴集团技术团队的集体智慧结晶和经验总结,经历了多次大规模一线实战的检验及不断完善,系统化地整理成册,回馈给广大开发者.现代软件行业的高速发展对开发者的综合素质要求越来越高,因为不仅是编程知识点,其它维度的知识点也会影响到软件的最终交付质量.比如:数据库的表结构和索引设计缺陷可能带来软件上的架构缺陷或性能风险:工程结构混乱导致后续维护艰难:没有鉴权的漏洞代码易被黑客攻击等等.所以本手册以Java开发者为中心视角,划分为编程规约.异常日志.单元测

阿里巴巴 Java 开发手册 1.4.0

一.编程规约(一) 命名风格1. [强制]代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束.反例: _name / __name / $name / name_ / name$ / name__2. [强制]代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式.说明:正确的英文拼写和语法可以让阅读者易于理解,避免歧义.注意,即使纯拼音命名方式也要避免采用.正例: alibaba / taobao / youku / hangzhou 等国际通用的名称,可视同

阿里巴巴Java开发手册(华山版)

title: Java开发规范 date: 2018-10-08 14:01:59 tags: Java categories: Java --- 前言 本文章是将网上PDF版本的Java开发规范转换成的Markdown版本 Java 开发手册 版本号 作者 日期 备注 1.4.0 阿里巴巴集团技术团队 2018. 5. 20 增加设计规约(详尽版) 一.编程规约 (一) 命名风格 [强制]代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号 结束. 反例:_name __name

阿里巴巴 Java 开发手册

一.编程规约(一) 命名规约1. [强制] 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束. 反例: _name _name _name / __name __name __name / $Object $Object $Object $Object / name_ name_ name_ / name$name$ name$ / Object$Object$ Object$ Object$2. [强制] 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方

阿里巴巴Java 开发手册编程规约之异常日志

(一)异常处理1. [强制]Java 类库中定义的一类 RuntimeException 可以通过预先检查进行规避,而不应该通过 catch 来处理,比如: IndexOutOfBoundsException, NullPointerException 等等.说明: 无法通过预检查的异常除外,如在解析一个外部传来的字符串形式数字时,通过 catchNumberFormatException 来实现.正例: if (obj != null) {...}反例: try { obj.method()

阿里巴巴Java开发手册———个人追加的见解和补充(一)

先上干货,<阿里巴巴Java开发手册>的下载地址 https://yq.aliyun.com/articles/69327?spm=5176.100239.blogcont69327.158.xUUgiz 下面分几个部分对这个手册进行说明,都是个人的见解,本人技术一般,如果有错误或者不妥,请评论指出,虚心接受,提前感谢了. 建议边看手册,边食用以下说明,效果比较好. 前言 首先当我第一次看见这个的手册的时候或许和和你们是一样激动的,因为在java行业内我还没有看见有中文的规范,也可能是我读书少

读阿里巴巴Java开发手册v1.2.0之编程规约有感【架构篇】

 不为过去蹉跎,改变当下. 为什么开篇就送这么一句话给大家,我相信很多处于1-3年码龄的哥们儿们,在平时的编码历程中编码的个性可能是多彩的,每个人都有每个人特定的风格,但是我们现在这么随意写,以后这么随意写,好没问题,但是等你离开这个公司了或者是去开发别的项目了,再等别人过来接手维护你一手写出来的这段个性十足的代码时,那么你的右眼皮时不时地就会跳,因果我就不说了~~ 所以我建议看到这篇博文的朋友们,或许你稍微改变一下你的编码风格,遵从一套好的编码规约对己对人都是有好处的.可能朋友的公司也有专门的

阿里巴巴Java开发手册上常规的编程命名总结

阿里巴巴Java开发手册上常规的编程命名总结:1.类名必须驼峰.例子:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion 2.方法名.参数名.成员变量.局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰形式.例子:localValue / getHttpMessage() / inputUserId 3.常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长.例子:MAX_STOCK_COUNT