POJO類中布爾類型的變量都不要加is前綴詳解


前言

對應阿里巴巴開發手冊第一章的命名風格的第八條。

【強制】 POJO類中布爾類型的變量都不要加is前綴,否則部分框架解析會引起序列化錯誤。

反例:定義為基本數據類型Boolean isDeleted; 的屬性,它的方法名稱也是 isDeleted(),
RPC框架在反向解析的時候,“誤以為”對應的屬性名稱是deleted,導致屬性獲取不到拋出異常。

我對這個反例感覺有點怪怪的,基本數據類型Boolean?而且Boolean生成的getter方法是getXxx(),boolean生成的getter方法是isXxx(),疑惑,不知道是不是手冊寫錯了還是我錯了。

我認為這條很重要很重要。前后端傳遞數據時,就有可能因為布爾類型變量的命名,導致前后端傳送數據時傳遞失敗。 因為這個布爾類型的數據解析不一致,在后端該命名是isXxx,而在前端發送表單是傳送的數據是isXxx,但是響應時卻是Xxx,說明傳輸到后端時會解析成Xxx(像spring會根據getter和setter來解析POJO類,而當我創建的是isXxx,那么自動生成的getter方法會是isXxx方法,然后解析時會去掉is,所以變成了Xxx),而后端原本是isXxx,導致找不到該屬性,所以值也傳遞不了。
在這里插入圖片描述
在這里插入圖片描述
所以這是一個致命問題,但是可以避免啊。

詳解

在Java中布爾類型有基本數據類型和包裝類,所以有四種方式來定義一個布爾類型的變量:

boolean isLive;
boolean live;
Boolean isLive;
Boolean live;

首先來總結上面的區別:

  1. 四種中有兩種是boolean,而另外兩種是Boolean。可不要傻乎乎的說這兩個類型是一樣的,正確的是它們是有區別的。
  2. 四種中有兩種變量名是以is開頭,另外兩種沒有。

先根據手冊,可得布爾類型的變量命名不使用以is開頭的。為什么?

來看看下面代碼:

// boolean isLive
class People {
    private boolean isLive;

    public boolean isLive() {
        return isLive;
    }

    public void setLive(boolean live) {
        isLive = live;
    }
}

// boolean live
class People {
    private boolean live;

    public boolean isLive() {
        return live;
    }

    public void setLive(boolean live) {
        this.live = live;
    }
}

// Boolean isLive
class People {
    private Boolean isLive;

    public Boolean getLive() {
        return isLive;
    }

    public void setLive(Boolean live) {
        isLive = live;
    }
}

// Boolean live
class People {
    private Boolean live;

    public Boolean getLive() {
        return live;
    }

    public void setLive(Boolean live) {
        this.live = live;
    }
}

噫,通過代碼和idea自動生成的getter和setter方法,可得到:

  1. boolean類型的getter方法是isXxx()的形式。
  2. Boolean類型的getter方法是getXxx()的形式。
  3. 這兩個類型的setter方法相同。

其實isXxx()方法和getXxx()都是一樣的,都是會解析成Xxx,但是只有布爾類型才有isXxx()方法,所以兩種可以互換。比如把Boolean類型的getXxx改成isXxx,或者把boolean類型的isXxx改成getXxx。

一般情況下,其實布爾類型使用isXxx或不使用沒有區別,但是在序列化時才可以看到區別,特別是現在開發中,都是序列化或者自動序列化的,所以可能因為命名的問題導致錯誤。

現在就先解決使用isXxx和使用Xxx的問題,我們拿常用的JSON序列化舉例,比如看看fastJson、jackson和Gson之間有何區別:(還好參考了別人的博文不然我自己舉的例子只是fastJson)

// boolean isLive
class People1 implements Serializable {
    private boolean isLive;

    public boolean isLive() {
        return isLive;
    }

    public void setLive(boolean live) {
        isLive = live;
    }
}
// boolean live
class People2 implements Serializable {
    private boolean live;

    public boolean isLive() {
        return live;
    }

    public void setLive(boolean live) {
        this.live = live;
    }
}
public class PeopleTest {
    public static void main(String[] args) throws JsonProcessingException {
        ObjectMapper jackson = new ObjectMapper();
        Gson gson = new Gson();

        People1 people1 = new People1();
        people1.setLive(true);
        System.out.println("boolean isLive");
        System.out.println("fastJson:" + JSON.toJSONString(people1));

        System.out.println("jackson:" + jackson.writeValueAsString(people1));

        System.out.println("Gson:" + gson.toJson(people1));

        People2 people2 = new People2();
        people2.setLive(true);
        System.out.println("boolean live");
        System.out.println("fastJson:" + JSON.toJSONString(people2));
        System.out.println("jackson:" + jackson.writeValueAsString(people2));
        System.out.println("Gson:" + gson.toJson(people2));
    }
}

去maven倉庫訪問速度有時太慢了,所以下面提供了fastJson、jackson和Gson的倉庫鏈接,自己拿去耍耍。

        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.8</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
            <version>2.8.5</version>
        </dependency>

運行結果:

boolean isLive
fastJson:{"live":true}
jackson:{"live":true}
Gson:{"isLive":true}
boolean live
fastJson:{"live":true}
jackson:{"live":true}
Gson:{"live":true}

根據這些結果就可以得出:

  1. 使用布爾類型的變量命名為isXxx,fastJson和jackson在把對象序列化成json字符串時,是通過反射遍歷出該類中的所有getter方法(或isXxx方法),得到isLive()方法,然后根據JavaBean規范,它會認為這是live屬性,然后序列化成json。
  2. 而Gson並不是這么做的,他是通過反射遍歷該類中的所有屬性,並把其值序列化成json。

可得由於不同的序列化工具,在進行序列化的時候使用到的策略是不一樣的,所以,對於同一個類的同一個對象的序列化結果可能是不同的。

現在,不同的序列化框架得到的json內容並不相同,如果對於同一個對象,我使用fastjson進行序列化,再使用Gson反序列化會發生什么?

// boolean isLive
class People1 implements Serializable {
    private boolean isLive;

    public boolean isLive() {
        return isLive;
    }

    public void setLive(boolean live) {
        isLive = live;
    }

    @Override
    public String toString() {
        return "People1{" +
                "isLive=" + isLive +
                '}';
    }
}
// boolean live
class People2 implements Serializable {
    private boolean live;

    public boolean isLive() {
        return live;
    }

    public void setLive(boolean live) {
        this.live = live;
    }

    @Override
    public String toString() {
        return "People2{" +
                "live=" + live +
                '}';
    }
}
public class PeopleTest {
    public static void main(String[] args) throws JsonProcessingException {
        Gson gson = new Gson();

        People1 people1 = new People1();
        people1.setLive(true);


        People2 people2 = new People2();
        people2.setLive(true);


        System.out.println("boolean isLive::" + gson.fromJson(JSON.toJSONString(people1), People1.class));
        System.out.println("boolean live::" + gson.fromJson(JSON.toJSONString(people2), People2.class));

    }
}

結果:

在這里插入圖片描述

會發現,在People1類中已經把isLive設置成true,現在怎么變成false?

原因是因為JSON框架通過掃描所有的getter后發現有一個isLive方法,然后根據JavaBeans的規范,解析出變量名為live,把model對象序列化城字符串后內容為{“live”: true}。
然后根據{“live”: true}這個json串,Gson框架在通過解析后,通過反射尋找People類中的live屬性,但是People類中只有isLive屬性,找不到對應的屬性,所以,最終反序列化后的People類的對象中,isLive則會使用默認值false。

所以這樣會導致前台的布爾類型的數據傳送不到后端,因為前端傳過來的會把命名為isXxx的變量解析成Xxx,而我們POJO類中的布爾類型的屬性是isXXX,導致在POJO類中找不到Xxx,所以就導致傳遞值失敗。我們必須避免這樣的致命問題。

最終,解釋了:POJO類中布爾類型的變量都不要加is前綴


下面再來解釋,是使用Boolean類型還是使用boolean。這里可以看開發手冊 1.4 OOP規約的第11條

所有的POJO類屬性必須使用包裝數據類型。
RPC方法的返回值和參數必須使用包裝數據類型。
所有的局部變量使用基本數據類型。

為什么?

很簡單,看這兩種類型的區別,boolean類型的默認值為false;而Boolean類型的默認值是null。

舉個栗子:在學校考試的時候,我們要錄入學生成績,假設使用Integer類型來錄入,那么學生要是沒有來考試,那么該成績就是null,學生考0分,說明學生有來考試,但是考了0分;使用int類型來錄入,注意int類型默認值為0,那么成績如果是0,我們不能確定該學生是沒有來考試的呢?還是有來考試但考了0分。

參考:
為什么阿里巴巴開發手冊強制規定POJO 類中布爾類型的變量,都不要加 is 前綴


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM