JavaBean中Boolean類型的字段名不要用isXxx(轉)

add by zhj: 之前看阿里出品的Java開發手冊中提到Java Bean中Boolean類型字段名不要用isXxx命名,一直不明白原因。這篇文章詳細說明了原因。我對原文略微進行了修改,將fastjson庫改爲fastjson2,但對序列化反序列化結果沒有影響。

原文:https://mp.weixin.qq.com/s/b1q779XdWyRe0QyGxhzhdg

作者:Hollis

在日常開發中,我們會經常要在類中定義布爾類型的變量,比如在給外部系統提供一個RPC接口的時候,我們一般會定義一個字段表示本次請求是否成功的。

關於這個”本次請求是否成功”的字段的定義,我見過很多不同的開發者,定義的方式都不同,尤其是在屬性的命名上,有人用 success,有人用 isSuccess 表示。

從語義上面來講,兩種命名方式都可以講的通,並且也都沒有歧義。那麼還有什麼原則可以參考來讓我們做選擇呢。

根據JavaBeans 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);

success方法的 getter 應該是isSuccess/getSuccess,而isSuccess的getter 應該是isIsSuccess/getIsSuccess。

但是很多人,在使用isSuccess作爲屬性名的時候,還是會採用isSuccess/getSuccess作爲 getter 方法名,尤其是現在的很多 IDE 在默認生成 getter 的時候也是會生成isSuccess。

在一般情況下,其實是沒有影響的。但是有一種特殊情況就會有問題,那就是發生序列化的時候可能會導致參數轉換異常。

我們先來定義一個 JavaBean:

class Model 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";

    }

    @Override
    public String toString() {
        return "Model[isSuccess=" + isSuccess + "]";
    }
}

 

在這個 JavaBean 中,有一個成員變量isSuccess,三個方法,分別是IDE幫我們自動生成的isSuccess和setSuccess,另外一個是作者自己增加的一個符合getter命名規範的方法。

我們分別使用不同的 JSON 序列化工具來對這個類的對象進行序列化和反序列化:

public class BooleanMainTest {

     public static void main(String[] args) throws IOException {

         //定一個Model類型

         Model model = new Model();

         model.setSuccess(true);



         //使用fastjson2(2.0.33)序列化model成字符串並輸出

         System.out.println("Serializable Result With fastjson2 :" + JSON.toJSONString(model));



         //使用Gson(2.10.1)序列化model成字符串並輸出

         Gson gson =new Gson();

         System.out.println("Serializable Result With Gson :" +gson.toJson(model));



         //使用jackson(2.10.1)序列化model成字符串並輸出

         ObjectMapper om = new ObjectMapper();

         System.out.println("Serializable Result With jackson :" +om.writeValueAsString(model));

     }

}

以上代碼輸出結果:

Serializable Result With fastjson2 :{"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}

可以看到,由於不同的序列化工具,在進行序列化的時候使用到的策略是不一樣的,所以,對於同一個類的同一個對象的序列化結果可能是不同的。那麼,如果我們把一個對象使用fastjson進行序列化,再使用Gson反序列化會發生什麼呢?

 public class BooleanMainTest {

     public static void main(String[] args) throws IOException {

         Model model = new Model();

         model.setSuccess(true);

         Gson gson =new Gson();

         System.out.println(gson.fromJson(JSON.toJSONString(model),Model.class));

     }

 }

以上代碼,輸出結果:

Model[isSuccess=false]

這和我們預期的結果完全相反,原因是因爲JSON框架通過掃描所有的getter後發現有一個isSuccess方法,然後根據JavaBeans的規範,解析出變量名爲success,把model對象序列化城字符串後內容爲{"success":true}。

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

但是,一旦以上代碼發生在生產環境,這絕對是一個致命的問題。

所以,作爲開發者,我們應該想辦法儘量避免這種問題的發生。

所以,建議大家使用success而不是 isSuccess 這種形式。這樣,該類裏面的成員變量時success,getter方法是isSuccess,這是完全符合JavaBeans規範的。無論哪種序列化框架,執行結果都一樣。就從源頭避免了這個問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章