為什麼阿里巴巴開發手冊中強制要求 POJO 類使用包裝型別?NPE問題防範

寧在春發表於2021-10-15

封面:學校內的秋天

背景:寫這個的原因,也是我這兩天湊巧看到的,雖然我一直有 alibaba Java 開發手冊,也看過不少次,但是一直沒有注意過這個問題?

屬於那種看過,但又沒完全看過?

一起來看看吧衝咯?

hxdm,我寫不出小故事?,但是可不可以看在我寫了不少,還算實用的份上,給個贊?啊。

在這裡請xdm 喝 ?啦

一、前言?

image-20211013210702451

今天在寫一個AdvertVO類時,我當時用 id 是直接給了個 long,沒有使用用包裝型別,然後 idea 裡面的 Alibaba Java Coding Guidelines 外掛就直接給了個黃色波浪線,ALT+ENTER一看,阿里巴巴Java開發手冊 的提示說:

關於基本資料型別與包裝資料型別的使用標準如下:
 1) 所有的POJO類屬性必須使用包裝資料型別。
 2) RPC方法的返回值和引數必須使用包裝資料型別。
 3) 所有的區域性變數推薦使用基本資料型別。

說明:POJO類屬性沒有初值是提醒使用者在需要使用時,必須自己顯式地進行賦值,任何NPE問題,或者入庫檢查,都由使用者來保證

我剛看的時候,稍稍有點點沒完全理解這個意思(可能是我比較菜,沒有經歷過這樣的場景),然後為了搞懂自己心裡的小疑惑?,就?

image-20211013212155710

接下來我們弄一個簡單的例子來理解理解,之後再聊聊實際發生的場景,以及會產生的危害

二、例子?

public class Main {
    private static Integer a1;
    private static int a2;

    private static Boolean b1;
    private static boolean b2;
  
    public static void main(String[] args) {
        System.out.println("Integer ==> a1:"+a1);
        System.out.println("int     ==> a2:"+a2);
        System.out.println("Boolean ==> b1:"+b1);
        System.out.println("boolean ==> b2:"+b2);
    }
    /**
     * 結果:
     * Integer ==> a1:null
     * int     ==> a2:0
     * Boolean ==> b1:null
     * boolean ==> b2:false
     */
}

所有的包裝型別在我們沒有賦值的時候,都是直接預設 null 值的,而基本型別都會初始化一個預設值。

也就是說,包裝型別的預設值都是null,而基本資料型別的預設值是一個固定值,如boolean是false,byte、short、int、long是0,float是0.0f等;

?‍?可能 xdm 平時有注意到,但又沒有完全注意到,用 基本型別和包裝型別之間的區別。下面我們用場景說一說區別:?‍?

三、場景?

【正例】:資料庫的查詢結果可能是 null,因為自動拆箱,用基本資料型別接收有 NPE 風險。( NPE 下文有解釋)

【反例】:某業務的交易報表上顯示成交總額漲跌情況,即正負 x%,x 為基本資料型別,呼叫的 RPC 服務,呼叫不成功時,返回的是預設值,頁面顯示為 0%,這是不合理的,應該顯示成中劃線-。所以包裝資料型別 的 null 值,能夠表示額外的資訊,如:遠端呼叫失敗,異常退出。

1)場景一⛵

我們再舉一個扣費的例子,我們做一個扣費系統,扣費時需要從外部的定價系統中讀取一個費率的值,我們預期該介面的返回值中會包含一個浮點型的費率欄位。當我們取到這個值得時候就使用公式:金額*費率=費用 進行計算,計算結果進行劃扣。

如果由於計費系統異常,他可能會返回個預設值,如果這個欄位是 Double 型別的話,該預設值為 null ,如果該欄位是 double 型別的話,該預設值為 0.0。

如果扣費系統對於該費率返回值沒做特殊處理的話,拿到null值進行計算會直接報錯,阻斷程式。拿到 0.0可能就直接進行計算,得出介面為 0 後進行扣費了。這種異常情況就無法被感知。

有人說,那我可以對 0.0 做特殊判斷,如果是 0 一樣可以阻斷報錯啊。但是,這時候就會產生一個問題,如果允許費率是 0 的場景又怎麼處理呢?(如下例)

一個小小結論:使用基本型別可能會在一定程度上增大系統的複雜性,讓坑變得越來越多。還有這種使用包裝型別定義變數的方式,通過異常來阻斷程式的執行,進而可以被立馬識別到這種綫上問題。但是我們如果使用基本資料型別的話,系統可能認為無異常,從而繼續執行。只能被動的測試出現問題,更甚的是如果是線上出現這種問題,我想可能...都明白哈。

2)場景二?

簡單來說就是我們如果自定義了一個 Student 類,其中有一個屬性是成績 score .

如果用 Integer 而不用 int 定義,一次考試,學生可能沒考,值是null,也可能考了,但考了0分,值是0.

public class Student  {

    private  Integer score;

    private  int score;
}

請注意這兩個表達的狀態明顯不一樣 。如果我們用包裝型別的話,null的話證明沒有考,0的話證明考了0分;但是如果我們用基本型別的話,這兩種情況都是一個樣的,沒法區分的。

四、NPE 問題?

【推薦】防止 NPE,是程式設計師的基本修養,注意 NPE 產生的場景:

NPE,指為基本型別的資料返回null值,防止NPE是程式設計師的基本休養。所有NPE的場景:

  1. 返回型別為基本資料型別,return包裝資料型別的物件時,自動拆箱有可能產生NPE。

    public int f() {
          return Integer 物件;
     } 
    如果為null,自動解箱拋NPE。
    
  2. 資料庫的查詢結果可能為 null。
  3. 集合裡的元素即使 isNotEmpty,取出的資料元素也可能為 null。
  4. 遠端呼叫返回物件時,一律要求進行空指標判斷,防止 NPE。
  5. 對於 Session 中獲取的資料,建議進行 NPE 檢查,避免空指標。
  6. 級聯呼叫 obj.getA().getB().getC() ;一連串呼叫,易產生 NPE。

正例:使用 JDK8 的 Optional 類來防止 NPE 問題。瞭解?JDK8 Optional 類

五、自言自語?

你好,我是博主寧在春segmentfault主頁

希望本篇文章能讓你感到有所收穫!!!

我們:待別日相見時,都已有所成

歡迎大家一起討論問題?,躺了?

image-20211014091239193

參考:Alibaba Java 開發手冊

相關文章