為什麼阿里巴巴Java開發手冊中強制要求介面返回值不允許使用列舉?

武培軒發表於2020-06-06

在閱讀《阿里巴巴Java開發手冊》時,發現有一條關於二方庫依賴中介面返回值不允許使用列舉型別的規約,具體內容如下:

在談論為什麼之前先來科普下什麼是二方庫,二方庫也稱作二方包,一般指公司內部發布到中央倉庫,可供公司內部其他應用依賴的庫(jar 包)。

那麼一方庫便是本工程內部子專案模組依賴的庫;三方庫為公司之外的開源庫,比如像 fastjson、easyexcel 這種。

下面我們就通過一個例子來看下為什麼阿里巴巴不允許返回列舉型別或者包含列舉型別的 POJO 物件。

比如星巴克提供了 0.0.1 版本的二方庫,定義了一個 Starbucks 類,裡面包含了列舉型別的 SizeEnum,裡面分別是中杯、大杯、特大杯。

public class Starbucks implements Serializable {
  private Long id;
  private String name;
  private Integer capacity;
  private SizeEnum sizeEum;
}

public enum SizeEnum {
  TALL(1),
  GRANDE(2),
  VENTI(3)
}

定義了一個服務類,實現了根據 id 獲取星巴克的方法:

public class StarbucksImpl implements StarbucksService {
  public Starbucks getStarbucksById(Long id) {
    Starbucks starbucks = new Starbucks();
    starbucks.setId(1L);
    starbucks.setName("Latte");
    starbucks.setCapacity(360);
    starbucks.setSizeEnum(SizeEnum.TALL);
    return starbucks;
  }
}

然後,星巴克的門店引入 0.0.1 這個版本 jar 包,然後賣的好好的:

public class StarbucksDemo {
  @Resource
  private StarbucksService starbucksService;
  
  public void getStarbucks() {
    Starbucks starbucks = starbucksService.getStarbucksById(1L);
    System.out.println(starbucks);
  }
}

有一天,老羅說要那個中等大小的中杯拿鐵,但是服務員說那是大杯,經過一番爭論,羅老師很是生氣。

於是星巴克升級到了 0.0.2 版本二方庫,在列舉類 SizeEnum 中新增了小杯,升級後的列舉類如下:

public enum SizeEnum {
  TALL(1),
  GRANDE(2),
  VENTI(3),
  SHORT(4)
}

同時服務類的介面方法也做了相應修改:

public class StarbucksImpl implements StarbucksService {
  public Starbucks getStarbucksById(Long id) {
    Starbucks starbucks = new Starbucks();
    starbucks.setId(1L);
    starbucks.setName("Latte");
    starbucks.setCapacity(240);
    starbucks.setSizeEnum(SizeEnum.SHORT);
    return starbucks;
  }
}

由於星巴克的門店比較多,有的還不知道這個新加的需求,因此返回結果中出現了 SHORT,但是 0.0.1 版本的二方庫中沒有小杯啊,所以就出問題了,也就是序列化失敗。

通過這個例子,我相信大家對列舉型別作為返回結果有了一定的理解,下面引用孤盡大佬在知乎的回答:

由於升級原因,導致雙方的列舉類不盡相同,在介面解析,類反序列化時出現異常。

Java 中出現的任何元素,在 Gosling 的角度都會有背後的思考和邏輯(儘管並非絕對完美,但 Java 的頂層抽象已經是天才級了),比如:介面、抽象類、註解、和本文提到的列舉。列舉有好處,型別安全,清晰直接,還可以使用等號來判斷,也可以用在 switch 中。它的劣勢也是明顯的,就是不要擴充套件。可是為什麼在返回值和引數進行了區分呢,如果不相容,那麼兩個都有問題,怎麼允許引數可以有列舉。當時的考慮,如果引數也不能用,那麼列舉幾乎無用武之地了。引數輸出,畢竟是本地決定的,你本地有的,傳送過去,向前相容是不會有問題的。但如果是介面返回,就比較噁心了,因為解析回來的這個列舉值,可能本地還沒有,這時就會丟擲序列化異常。

比如:你的本地列舉類,有一個天氣 Enum:SUNNY, RAINY, CLOUDY,如果根據天氣計算心情的方法:guess(WeatcherEnum xx),傳入這三個值都是可以的。返回值:Weather guess(引數),那麼對方運算後,返回一個 SNOWY,本地列舉裡沒有這個值,傻眼了。

總結

本文通過一個例項讓大家理解到列舉型別作為返回結果的坑,大家可以使用基本型別或者基本型別包裝類來替換掉列舉型別就可以避免掉這麼問題了。

大家對於這條規約​有什麼看法,也歡迎留言討論。​

最好的關係就是互相成就,大家的在看、轉發、留言三連就是我創作的最大動力。

參考

《Java開發手冊》泰山版

https://www.zhihu.com/question/52760637/answer/338584321

相關文章