注意由雙大括號匿名類引起的serialVersionUID編譯告警

Hackerleon發表於2020-10-27

問題描述

最近版本組織清理編譯告警,其中有這麼一條比較有意思,之前沒見過,拿出來說一說

serializable class anonymous com.demo.Main$1 has no definition of serialVersionUID

編譯告警指向了這段程式碼:

private static List<String> defaultAttrList = new ArrayList<String>() {
    {
        add(ResourceConsts.RES_NAME);
        add(ResourceConsts.RES_TYPE);
        add(ResourceConsts.RES_IP);
        add(ResourceConsts.RES_VERSION);
    }
};

乍一看好像沒什麼問題,我用雙大括號的方式定義並初始化了一個ArrayList,往裡面塞了幾個值,程式碼簡潔易懂。

但問題並沒有看起來那麼簡單,原因就在雙大括號

探究

雙大括號的寫法實際上建立了一個匿名類,我們將原始檔編譯後也會發現,生成了一個Main$1.class的檔案,它就對應這個匿名類。反編譯後的程式碼如下:

class Main$1 extends ArrayList<String> {
    Main$1() {
        this.add("1");// 10
    }// 11
}

可以看到,我們建立了一個名為Main$1的匿名類,繼承自ArrayList,而ArrayList的類定義如下:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable {
    // ......
}

正是因為ArrayList實現了Serializable介面,所以Main$1也需要定義serialVersionUID

解決方法

既然是由於匿名類引起的編譯告警,我們可以幹掉匿名類,用靜態域來初始化List,像下面這樣即可消除告警:

private static List<String> defaultAttrList = new ArrayList<>();
static {
    defaultAttrList.add(ResourceConsts.RES_NAME);
    defaultAttrList.add(ResourceConsts.RES_TYPE);
    defaultAttrList.add(ResourceConsts.RES_IP);
    defaultAttrList.add(ResourceConsts.RES_VERSION);
}

參考資料

  1. 永遠不要使用雙花括號初始化例項,否則就會OOM!

相關文章