溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

為什么不使用isSuccess作為變量名

發(fā)布時(shí)間:2021-12-31 14:00:23 來源:億速云 閱讀:168 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容介紹了“為什么不使用isSuccess作為變量名”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

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

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

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

boolean success
boolean isSuccess
Boolean success
Boolean isSuccess

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

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

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

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

success 還是 isSucces
為什么不使用isSuccess作為變量名

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


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

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

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

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

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

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

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

3y:看到這里的同學(xué)可能有點(diǎn)懵,為啥這里說“已經(jīng)達(dá)成一致共識使用基本類型boolean來定義成員變量” 。其實(shí)原作者就這篇文章寫了兩篇。至于為什么原作者贊同使用基本數(shù)據(jù)類型,可以看為什么贊同boolean使用基本數(shù)據(jù)類型。本文的后面也有相應(yīng)解釋。

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

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

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

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

但是,布爾類型的變量propertyName則是另外一套命名原則的:

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

為什么不使用isSuccess作為變量名

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

那這樣做會帶來什么問題呢。

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

序列化帶來的影響

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

/**

* @author Hollis

*/

public class BooleanMainTest {

   public static void main(String[] args) throws IOException {
       //定一個(gè)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中,只有一個(gè)成員變量即isSuccess,三個(gè)方法,分別是IDE幫我們自動生成的isSuccess和setSuccess,另外一個(gè)是作者自己增加的一個(gè)符合getter命名規(guī)范的方法。

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

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

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

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

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

但是Gson并不是這么做的,他是通過反射遍歷該類中的所有屬性,并把其值序列化成json:

{“isSuccess”:true}

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

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

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

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

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

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

Model3[isSuccess=false]

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

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

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

所以,作為開發(fā)者,我們應(yīng)該想辦法盡量避免這種問題的發(fā)生,對于POJO的設(shè)計(jì)者來說,只需要做簡單的一件事就可以解決這個(gè)問題了,那就是把isSuccess改為success。

這樣,該類里面的成員變量時(shí)success,getter方法是isSuccess,這是完全符合JavaBeans規(guī)范的。無論哪種序列化框架,執(zhí)行結(jié)果都一樣。就從源頭避免了這個(gè)問題。

引用一下R大關(guān)于阿里巴巴Java開發(fā)手冊中這條規(guī)定的評價(jià)

為什么不使用isSuccess作為變量名

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

Boolean還是boolean?
為什么不使用isSuccess作為變量名

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

boolean success
Boolean success

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

我們知道,boolean是基本數(shù)據(jù)類型,而Boolean是包裝類型。

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

我們來看一段簡單的代碼

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

class Model {
   /**
    * 定一個(gè)Boolean類型的success成員變量
    */
   private Boolean success;
   /**
    * 定一個(gè)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();
   }
}

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

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

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

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

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

為什么不使用isSuccess作為變量名

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

舉一個(gè)扣費(fèi)的例子,我們做一個(gè)扣費(fèi)系統(tǒng),扣費(fèi)時(shí)需要從外部的定價(jià)系統(tǒng)中讀取一個(gè)費(fèi)率的值,我們預(yù)期該接口的返回值中會包含一個(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)異常,他可能會返回個(gè)默認(rèn)值,如果這個(gè)字段是Double類型的話,該默認(rèn)值為null,如果該字段是double類型的話,該默認(rèn)值為0.0。

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

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

“為什么不使用isSuccess作為變量名”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI