自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

為什么強(qiáng)烈禁止開(kāi)發(fā)人員使用isSuccess作為變量名

開(kāi)發(fā) 后端
關(guān)于這個(gè)"本次請(qǐng)求是否成功"的字段的定義,其實(shí)是有很多種講究和坑的,稍有不慎就會(huì)掉入坑里,作者在很久之前就遇到過(guò)類似的問(wèn)題,本文就來(lái)圍繞這個(gè)簡(jiǎn)單分析一下。到底該如何定一個(gè)布爾類型的成員變量。

在日常開(kāi)發(fā)中,我們會(huì)經(jīng)常要在類中定義布爾類型的變量,比如在給外部系統(tǒng)提供一個(gè)RPC接口的時(shí)候,我們一般會(huì)定義一個(gè)字段表示本次請(qǐng)求是否成功的。

關(guān)于這個(gè)"本次請(qǐng)求是否成功"的字段的定義,其實(shí)是有很多種講究和坑的,稍有不慎就會(huì)掉入坑里,作者在很久之前就遇到過(guò)類似的問(wèn)題,本文就來(lái)圍繞這個(gè)簡(jiǎn)單分析一下。到底該如何定一個(gè)布爾類型的成員變量。

一般情況下,我們可以有以下四種方式來(lái)定義一個(gè)布爾類型的成員變量:

  1. boolean success 
  2. boolean isSuccess 
  3. Boolean success 
  4. Boolean isSuccess 

以上四種定義形式,你日常開(kāi)發(fā)中最常用的是哪種呢?到底哪一種才是正確的使用姿勢(shì)呢?

通過(guò)觀察我們可以發(fā)現(xiàn),前兩種和后兩種的主要區(qū)別是變量的類型不同,前者使用的是boolean,后者使用的是Boolean。

另外,***種和第三種在定義變量的時(shí)候,變量命名是success,而另外兩種使用isSuccess來(lái)命名的。

首先,我們來(lái)分析一下,到底應(yīng)該是用success來(lái)命名,還是使用isSuccess更好一點(diǎn)。

success 還是 isSuccess

到底應(yīng)該是用success還是isSuccess來(lái)給變量命名呢?從語(yǔ)義上面來(lái)講,兩種命名方式都可以講的通,并且也都沒(méi)有歧義。那么還有什么原則可以參考來(lái)讓我們做選擇呢。

在阿里巴巴Java開(kāi)發(fā)手冊(cè)中關(guān)于這一點(diǎn),有過(guò)一個(gè)『強(qiáng)制性』規(guī)定:

為什么強(qiáng)烈禁止開(kāi)發(fā)人員使用isSuccess作為變量名

那么,為什么會(huì)有這樣的規(guī)定呢?我們看一下POJO中布爾類型變量不同的命名有什么區(qū)別吧。

  1. class Model1 { 
  2.  private Boolean isSuccess; 
  3.  public void setSuccess(Boolean success) { 
  4.  isSuccess = success; 
  5.  } 
  6.  public Boolean getSuccess() { 
  7.  return isSuccess; 
  8.  } 
  9.  } 
  10. class Model2 { 
  11.  private Boolean success; 
  12.  public Boolean getSuccess() { 
  13.  return success; 
  14.  } 
  15.  public void setSuccess(Boolean success) { 
  16.  this.success = success; 
  17.  } 
  18. class Model3 { 
  19.  private boolean isSuccess; 
  20.  public boolean isSuccess() { 
  21.  return isSuccess; 
  22.  } 
  23.  public void setSuccess(boolean success) { 
  24.  isSuccess = success; 
  25.  } 
  26. class Model4 { 
  27.  private boolean success; 
  28.  public boolean isSuccess() { 
  29.  return success; 
  30.  } 
  31.  public void setSuccess(boolean success) { 
  32.  this.success = success; 
  33.  } 

以上代碼的setter/getter是使用Intellij IDEA自動(dòng)生成的,仔細(xì)觀察以上代碼,你會(huì)發(fā)現(xiàn)以下規(guī)律:

  • 基本類型自動(dòng)生成的getter和setter方法,名稱都是isXXX()和setXXX()形式的。
  • 包裝類型自動(dòng)生成的getter和setter方法,名稱都是getXXX()和setXXX()形式的。

既然,我們已經(jīng)達(dá)成一致共識(shí)使用基本類型boolean來(lái)定義成員變量了,那么我們?cè)賮?lái)具體看下Model3和Model4中的setter/getter有何區(qū)別。

我們可以發(fā)現(xiàn),雖然Model3和Model4中的成員變量的名稱不同,一個(gè)是success,另外一個(gè)是isSuccess,但是他們自動(dòng)生成的getter和setter方法名稱都是isSuccess和setSuccess。

Java Bean中關(guān)于setter/getter的規(guī)范

關(guān)于Java Bean中的getter/setter方法的定義其實(shí)是有明確的規(guī)定的,根據(jù)JavaBeans(TM) Specification規(guī)定,如果是普通的參數(shù)propertyName,要以以下方式定義其setter/getter:

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

但是,布爾類型的變量propertyName則是單獨(dú)定義的:

  1. public boolean is<PropertyName>(); 
  2. public void set<PropertyName>(boolean m); 
為什么強(qiáng)烈禁止開(kāi)發(fā)人員使用isSuccess作為變量名

通過(guò)對(duì)照這份JavaBeans規(guī)范,我們發(fā)現(xiàn),在Model4中,變量名為isSuccess,如果嚴(yán)格按照規(guī)范定義的話,他的getter方法應(yīng)該叫isIsSuccess。但是很多IDE都會(huì)默認(rèn)生成為isSuccess。

那這樣做會(huì)帶來(lái)什么問(wèn)題呢。

在一般情況下,其實(shí)是沒(méi)有影響的。但是有一種特殊情況就會(huì)有問(wèn)題,那就是發(fā)生序列化的時(shí)候。

序列化帶來(lái)的影響

關(guān)于序列化和反序列化請(qǐng)參考Java對(duì)象的序列化與反序列化。我們這里拿比較常用的JSON序列化來(lái)舉例,看看看常用的fastJson、jackson和Gson之間有何區(qū)別:

  1. public class BooleanMainTest { 
  2.  public static void main(String[] args) throws IOException { 
  3.  //定一個(gè)Model3類型 
  4.  Model3 model3 = new Model3(); 
  5.  model3.setSuccess(true); 
  6.  //使用fastjson(1.2.16)序列化model3成字符串并輸出 
  7.  System.out.println("Serializable Result With fastjson :" + JSON.toJSONString(model3)); 
  8.  //使用Gson(2.8.5)序列化model3成字符串并輸出 
  9.  Gson gson =new Gson(); 
  10.  System.out.println("Serializable Result With Gson :" +gson.toJson(model3)); 
  11.  //使用jackson(2.9.7)序列化model3成字符串并輸出 
  12.  ObjectMapper om = new ObjectMapper(); 
  13.  System.out.println("Serializable Result With jackson :" +om.writeValueAsString(model3)); 
  14.  } 
  15. class Model3 implements Serializable { 
  16.  private static final long serialVersionUID = 1836697963736227954L; 
  17.  private boolean isSuccess; 
  18.  public boolean isSuccess() { 
  19.  return isSuccess; 
  20.  } 
  21.  public void setSuccess(boolean success) { 
  22.  isSuccess = success; 
  23.  } 
  24.  public String getHollis(){ 
  25.  return "hollischuang"
  26.  } 

以上代碼的Model3中,只有一個(gè)成員變量即isSuccess,三個(gè)方法,分別是IDE幫我們自動(dòng)生成的isSuccess和setSuccess,另外一個(gè)是作者自己增加的一個(gè)符合getter命名規(guī)范的方法。

以上代碼輸出結(jié)果:

  1. Serializable Result With fastjson :{"hollis":"hollischuang","success":true
  2. Serializable Result With Gson :{"isSuccess":true
  3. Serializable Result With jackson :{"success":true,"hollis":"hollischuang"

在fastjson和jackson的結(jié)果中,原來(lái)類中的isSuccess字段被序列化成success,并且其中還包含hollis值。而Gson中只有isSuccess字段。

我們可以得出結(jié)論:fastjson和jackson在把對(duì)象序列化成json字符串的時(shí)候,是通過(guò)反射遍歷出該類中的所有g(shù)etter方法,得到getHollis和isSuccess,然后根據(jù)JavaBeans規(guī)則,他會(huì)認(rèn)為這是兩個(gè)屬性hollis和success的值。直接序列化成json:{"hollis":"hollischuang","success":true}

但是Gson并不是這么做的,他是通過(guò)反射遍歷該類中的所有屬性,并把其值序列化成json:{"isSuccess":true}

可以看到,由于不同的序列化工具,在進(jìn)行序列化的時(shí)候使用到的策略是不一樣的,所以,對(duì)于同一個(gè)類的同一個(gè)對(duì)象的序列化結(jié)果可能是不同的。

前面提到的關(guān)于對(duì)getHollis的序列化只是為了說(shuō)明fastjson、jackson和Gson之間的序列化策略的不同,我們暫且把他放到一邊,我們把他從Model3中刪除后,重新執(zhí)行下以上代碼,得到結(jié)果:

  1. Serializable Result With fastjson :{"success":true
  2. Serializable Result With Gson :{"isSuccess":true
  3. Serializable Result With jackson :{"success":true

現(xiàn)在,不同的序列化框架得到的json內(nèi)容并不相同,如果對(duì)于同一個(gè)對(duì)象,我使用fastjson進(jìn)行序列化,再使用Gson反序列化會(huì)發(fā)生什么?

  1. public class BooleanMainTest { 
  2.  public static void main(String[] args) throws IOException { 
  3.  Model3 model3 = new Model3(); 
  4.  model3.setSuccess(true); 
  5.  Gson gson =new Gson(); 
  6.  System.out.println(gson.fromJson(JSON.toJSONString(model3),Model3.class)); 
  7.  } 
  8. class Model3 implements Serializable { 
  9.  private static final long serialVersionUID = 1836697963736227954L; 
  10.  private boolean isSuccess; 
  11.  public boolean isSuccess() { 
  12.  return isSuccess; 
  13.  } 
  14.  public void setSuccess(boolean success) { 
  15.  isSuccess = success; 
  16.  } 
  17.  @Override 
  18.  public String toString() { 
  19.  return new StringJoiner(", ", Model3.class.getSimpleName() + "[""]"
  20.  .add("isSuccess=" + isSuccess) 
  21.  .toString(); 
  22.  } 

以上代碼,輸出結(jié)果:

  1. Model3[isSuccess=false

這和我們預(yù)期的結(jié)果完全相反,原因是因?yàn)镴SON框架通過(guò)掃描所有的getter后發(fā)現(xiàn)有一個(gè)isSuccess方法,然后根據(jù)JavaBeans的規(guī)范,解析出變量名為success,把model對(duì)象序列化城字符串后內(nèi)容為{"success":true}。

根據(jù){"success":true}這個(gè)json串,Gson框架在通過(guò)解析后,通過(guò)反射尋找Model類中的success屬性,但是Model類中只有isSuccess屬性,所以,最終反序列化后的Model類的對(duì)象中,isSuccess則會(huì)使用默認(rèn)值false。

但是,一旦以上代碼發(fā)生在生產(chǎn)環(huán)境,這絕對(duì)是一個(gè)致命的問(wèn)題。

所以,作為開(kāi)發(fā)者,我們應(yīng)該想辦法盡量避免這種問(wèn)題的發(fā)生,對(duì)于POJO的設(shè)計(jì)者來(lái)說(shuō),只需要做簡(jiǎn)單的一件事就可以解決這個(gè)問(wèn)題了,那就是把isSuccess改為success。這樣,該類里面的成員變量時(shí)success,getter方法是isSuccess,這是完全符合JavaBeans規(guī)范的。無(wú)論哪種序列化框架,執(zhí)行結(jié)果都一樣。就從源頭避免了這個(gè)問(wèn)題。

引用以下R大關(guān)于阿里巴巴Java開(kāi)發(fā)手冊(cè)這條規(guī)定的評(píng)價(jià)(https://www.zhihu.com/question/55642203):

為什么強(qiáng)烈禁止開(kāi)發(fā)人員使用isSuccess作為變量名 

所以,在定義POJO中的布爾類型的變量時(shí),不要使用isSuccess這種形式,而要直接使用success!

Boolean還是boolean?

前面我們介紹完了在success和isSuccess之間如何選擇,那么排除錯(cuò)誤答案后,備選項(xiàng)還剩下:

  1. boolean success 
  2. Boolean success 

那么,到底應(yīng)該是用Boolean還是boolean來(lái)給定一個(gè)布爾類型的變量呢?

我們知道,boolean是基本數(shù)據(jù)類型,而B(niǎo)oolean是包裝類型。關(guān)于基本數(shù)據(jù)類型和包裝類之間的關(guān)系和區(qū)別請(qǐng)參考一文讀懂什么是Java中的自動(dòng)拆裝箱

那么,在定義一個(gè)成員變量的時(shí)候到底是使用包裝類型更好還是使用基本數(shù)據(jù)類型呢?

我們來(lái)看一段簡(jiǎn)單的代碼:

  1.  /** 
  2.  * @author Hollis 
  3.  */ 
  4. public class BooleanMainTest { 
  5.  public static void main(String[] args) { 
  6.  Model model1 = new Model(); 
  7.  System.out.println("default model : " + model1); 
  8.  } 
  9. class Model { 
  10.  /** 
  11.  * 定一個(gè)Boolean類型的success成員變量 
  12.  */ 
  13.  private Boolean success; 
  14.  /** 
  15.  * 定一個(gè)boolean類型的failure成員變量 
  16.  */ 
  17.  private boolean failure; 
  18.  /** 
  19.  * 覆蓋toString方法,使用Java 8 的StringJoiner 
  20.  */ 
  21.  @Override 
  22.  public String toString() { 
  23.  return new StringJoiner(", ", Model.class.getSimpleName() + "[""]"
  24.  .add("success=" + success) 
  25.  .add("failure=" + failure) 
  26.  .toString(); 
  27.  } 

以上代碼輸出結(jié)果為:

  1. default model : Model[success=null, failure=false

可以看到,當(dāng)我們沒(méi)有設(shè)置Model對(duì)象的字段的值的時(shí)候,Boolean類型的變量會(huì)設(shè)置默認(rèn)值為null,而boolean類型的變量會(huì)設(shè)置默認(rèn)值為false。

即對(duì)象的默認(rèn)值是null,boolean基本數(shù)據(jù)類型的默認(rèn)值是false。

在阿里巴巴Java開(kāi)發(fā)手冊(cè)中,對(duì)于POJO中如何選擇變量的類型也有著一些規(guī)定:

為什么強(qiáng)烈禁止開(kāi)發(fā)人員使用isSuccess作為變量名

這里建議我們使用包裝類型,原因是什么呢?

舉一個(gè)扣費(fèi)的例子,我們做一個(gè)扣費(fèi)系統(tǒng),扣費(fèi)時(shí)需要從外部的定價(jià)系統(tǒng)中讀取一個(gè)費(fèi)率的值,我們預(yù)期該接口的返回值中會(huì)包含一個(gè)浮點(diǎn)型的費(fèi)率字段。當(dāng)我們?nèi)〉竭@個(gè)值得時(shí)候就使用公式:金額*費(fèi)率=費(fèi)用 進(jìn)行計(jì)算,計(jì)算結(jié)果進(jìn)行劃扣。

如果由于計(jì)費(fèi)系統(tǒng)異常,他可能會(huì)返回個(gè)默認(rèn)值,如果這個(gè)字段是Double類型的話,該默認(rèn)值為null,如果該字段是double類型的話,該默認(rèn)值為0.0。

如果扣費(fèi)系統(tǒng)對(duì)于該費(fèi)率返回值沒(méi)做特殊處理的話,拿到null值進(jìn)行計(jì)算會(huì)直接報(bào)錯(cuò),阻斷程序。拿到0.0可能就直接進(jìn)行計(jì)算,得出接口為0后進(jìn)行扣費(fèi)了。這種異常情況就無(wú)法被感知。

這種使用包裝類型定義變量的方式,通過(guò)異常來(lái)阻斷程序,進(jìn)而可以被識(shí)別到這種線上問(wèn)題。如果使用基本數(shù)據(jù)類型的話,系統(tǒng)可能不會(huì)報(bào)錯(cuò),進(jìn)而認(rèn)為無(wú)異常。

以上,就是建議在POJO和RPC的返回值中使用包裝類型的原因。

但是關(guān)于這一點(diǎn),作者之前也有過(guò)不同的看法:對(duì)于布爾類型的變量,我認(rèn)為可以和其他類型區(qū)分開(kāi)來(lái),作者并不認(rèn)為使用null進(jìn)而導(dǎo)致NPE是一種***的實(shí)踐。因?yàn)椴紶栴愋椭挥衪rue/false兩種值,我們完全可以和外部調(diào)用方約定好當(dāng)返回值為false時(shí)的明確語(yǔ)義。

后來(lái),作者單獨(dú)和《阿里巴巴Java開(kāi)發(fā)手冊(cè)》、《碼出高效》的作者——孤盡 單獨(dú)1V1(qing) Battle(jiao)了一下。最終達(dá)成共識(shí),還是盡量使用包裝類型。

但是,作者還是想強(qiáng)調(diào)一個(gè)我的觀點(diǎn),盡量避免在你的代碼中出現(xiàn)不確定的null值。

null何罪之有?

關(guān)于null值的使用,我在使用Optional避免NullPointerException、9 Things about Null in Java等文中就介紹過(guò)。

null是很模棱兩可的,很多時(shí)候會(huì)導(dǎo)致令人疑惑的的錯(cuò)誤,很難去判斷返回一個(gè)null代表著什么意思。

圖靈獎(jiǎng)得主Tony Hoare 曾經(jīng)公開(kāi)表達(dá)過(guò)null是一個(gè)糟糕的設(shè)計(jì)。

為什么強(qiáng)烈禁止開(kāi)發(fā)人員使用isSuccess作為變量名 

我把 null 引用稱為自己的十億美元錯(cuò)誤。它的發(fā)明是在1965 年,那時(shí)我用一個(gè)面向?qū)ο笳Z(yǔ)言( ALGOL W )設(shè)計(jì)了***個(gè)全面的引用類型系統(tǒng)。我的目的是確保所有引用的使用都是絕對(duì)安全的,編譯器會(huì)自動(dòng)進(jìn)行檢查。但是我未能抵御住誘惑,加入了Null引用,僅僅是因?yàn)閷?shí)現(xiàn)起來(lái)非常容易。它導(dǎo)致了數(shù)不清的錯(cuò)誤、漏洞和系統(tǒng)崩潰,可能在之后 40 年中造成了十億美元的損失。

當(dāng)我們?cè)谠O(shè)計(jì)一個(gè)接口的時(shí)候,對(duì)于接口的返回值的定義,盡量避免使用Boolean類型來(lái)定義。大多數(shù)情況下,別人使用我們的接口返回值時(shí)可能用if(response.isSuccess){}else{}的方式,如果我們由于忽略沒(méi)有設(shè)置success字段的值,就可能導(dǎo)致NPE(java.lang.NullPointerException),這明顯是我們不希望看到的。

所以,當(dāng)我們要定義一個(gè)布爾類型的成員變量時(shí),盡量選擇boolean,而不是Boolean。當(dāng)然,編程中并沒(méi)有絕對(duì)。

總結(jié)

本文圍繞布爾類型的變量定義的類型和命名展開(kāi)了介紹,最終我們可以得出結(jié)論,在定義一個(gè)布爾類型的變量,尤其是一個(gè)給外部提供的接口返回值時(shí),要使用success來(lái)命名,阿里巴巴Java開(kāi)發(fā)手冊(cè)建議使用封裝類來(lái)定義POJO和RPC返回值中的變量。但是這不意味著可以隨意的使用null,我們還是要盡量避免出現(xiàn)對(duì)null的處理的。

責(zé)任編輯:龐桂玉 來(lái)源: 今日頭條
相關(guān)推薦

2022-03-03 23:30:27

TypeScrip開(kāi)發(fā)前端

2023-12-25 07:58:46

Python開(kāi)發(fā).NET

2022-12-19 07:33:49

開(kāi)發(fā)人員谷歌制度

2018-07-09 14:05:16

編程語(yǔ)言PythonPipenv

2020-07-23 08:21:25

PHP開(kāi)發(fā)人員MVC

2011-05-05 17:57:18

軟件開(kāi)發(fā)

2021-11-01 22:19:29

開(kāi)發(fā)測(cè)試代碼

2021-04-18 18:12:07

Linux開(kāi)發(fā)操作系統(tǒng)

2020-06-22 07:18:21

Java語(yǔ)言開(kāi)發(fā)

2011-06-20 08:43:15

Windows 8開(kāi)發(fā)人員

2021-01-30 10:51:07

Python編程語(yǔ)言開(kāi)發(fā)

2023-09-04 08:20:00

2022-02-17 16:05:58

SQL開(kāi)發(fā)招聘

2023-01-11 12:14:50

NeoVimVim開(kāi)發(fā)

2020-05-25 16:36:19

開(kāi)發(fā)工具編碼

2022-10-25 15:51:40

2023-10-13 06:54:58

2012-06-18 15:05:54

開(kāi)發(fā)

2019-09-24 15:29:57

程序員開(kāi)發(fā)數(shù)據(jù)

2014-09-12 10:28:28

技術(shù)開(kāi)發(fā)程序員
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)