205K+程序員關注過的問題:爲何不該該使用Java的原始類型?

在逛 Stack Overflow 的時候,發現了一些訪問量像熊耳山同樣高的問題,好比說這個:爲何不該該使用Java的原始類型?訪問量足足有 205K+,這不得了啊!說明有不少不少的程序員被這個問題困擾過。實話實說吧,本文以前的就是其中之一。css

來回顧一下提問者的問題吧:java

Java 的原始類型是什麼?爲何不要使用原始類型?若是不能使用原始類型,有什麼更好的選擇呢?程序員

若是你們也被這個問題困擾過,或者正在被困擾,就請隨我來,我們肩並肩手拉手一塊兒梳理一下這個問題,並找出最佳答案。Duang、Duang、Duang,打怪進階嘍!web

0一、Java 的原始類型是什麼?

要理解 Java 的原始類型是什麼,能夠先看一下什麼是泛型安全

List<String> list = null;

其中 list 就是一個泛型,咱們一般稱之爲字符串(String)列表(List),也就是說 list 中只能放字符串類型的元素。微信

若是咱們按照下面這種方式聲明 list 的話,它就是一個原始類型。app

List list = null;

從 list 的聲明當中咱們能夠對比發現,原始類型沒有爲容器指定明確的元素類型,因此咱們能夠在容器中放入一個 String,也能夠放入一個 Integer,甚至任意的類型,就像下面這樣。spa

public class RawType {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("沉默王二");
        list.add(18);
        list.add(new RawType());
    }
}

注意哦,編譯器沒有任何提醒!這預示着 Java 這門強類型的語言居然有點弱類型的影子了。code

PS:關於 Java 中的類型術語,你們能夠參照下表。orm

術語 含義 舉例
Parameterized type 參數化類型 List<String>
Actual type parameter 實際類型參數 String
Generic type 泛型類型 List<E>
Formal type parameter 形式類型參數 E
Unbounded wildcard type 無限制通配符類型 List<?>
Raw type 原始類型 List
Bounded type parameter 限制類型參數 <E extends Number>
Bounded wildcard type 限制通配符類型 List<? extends Number>

0二、爲何不要使用原始類型?

你們可能會有一個疑惑,原始類型用起來很爽啊!由於不用關心放入 List 的元素究竟是什麼類型,想放什麼就能夠放什麼,不要太爽啊!

可當咱們想要從 List 中把元素取出來使用的時候,可就遇到大麻煩了。

List list = new ArrayList();
list.add("沉默王二");
list.add(18);
list.add(new RawType());

for (Object o : list ) {
    String s = (String) o;
    System.out.println(s);
}

上面這段代碼編譯的時候沒有任何問題,但輸出的時候就會拋出 ClassCastException

沉默王二
Exception in thread "mainjava.lang.ClassCastExceptionjava.lang.Integer cannot be cast to java.lang.String
    at com.cmower.java_demo.programcreek.RawType.main(RawType.java:14)

除非咱們使用 instanceof 關鍵字進行類型判斷,就像下面這樣。

List list = new ArrayList();
list.add("沉默王二");
list.add(18);
list.add(new RawType());

for (Object o : list ) {
    if (o instanceof String) {
        String s = (String) o;
        System.out.println(s);
    } else if (o instanceof Integer) {
        Integer i = (Integer) o;
        System.out.println(i);
    } else if (o instanceof RawType) {
        RawType raw = (RawType) o;
        System.out.println(raw);
    }
}

可假如代碼寫成這樣,可真真算得上是糟糕的代碼了。

一般來講,爲了代碼的安全性起見,咱們但願代碼的錯誤發生得越早越好,能在編譯時就不要在運行時。可以使用原始類型的時候,咱們發現錯誤一直到運行時纔可能會被檢出。

還記得《扁鵲見蔡桓公》的故事嗎?

扁鵲見蔡桓公,立有間。扁鵲曰:「君有疾在腠理,不治將恐深。」桓侯曰:「寡人無疾。」扁鵲出,桓侯曰:「醫之好治不病覺得功。」……居十日,扁鵲望桓侯而還走。桓侯故令人問之,扁鵲曰:「疾在腠理,湯熨之所及也;在肌膚,針石之所及也;在腸胃,火齊之所及也;在骨髓,司命之所屬,無奈何也。今在骨髓,臣是以無請也。」居五日,桓侯體痛,令人索扁鵲,已逃秦矣。桓侯遂死。

病情發現得越早,治療的可能性就越大。同理,代碼隱藏的問題發現的越晚,找出根源花費的精力就越大、時間就越多。

0三、有什麼更好的選擇呢?

若是不能使用原始類型,有什麼更好的選擇呢?

爲了讓 List 可以容納任意類型的元素,咱們可使用 List<Object>,儘管這並非一個最優的選擇。

List<Object> list = new ArrayList<>();
list.add("沉默王二");
list.add(18);
list.add(new RawType());

鵝鵝鵝,這樣的參數化類型 List<Object> 和原始類型 List 之間有區別嗎?

固然有了!

List<Object> 至少明確地告訴編譯器,該容器能夠存聽任意類型的對象,沒有丟失類型的安全性。

可能我這樣的解釋會遭到某些抨擊:「這不五十步笑百步嗎?呵呵。」但我要想表達的是登月男神阿姆斯特朗的那句話:「這是我我的的一小步,倒是人類的一大步。」能向前邁一步是一步啊。

那最優的選擇是什麼呢?

從一開始就爲 List 聲明具體的類型,好比說 List<String> list,當咱們嘗試放入一個 int 值的時候就會編譯出錯。

從另外一種層面上來講,這樣作削弱了程序的靈活性,但保證了程序的絕對安全性,以及在表達上的明確性。

0四、爲何 Java 容許使用原始類型?

既然原始類型是不安全的,那爲何 Java 一直容許使用原始類型呢?而且泛型擦除後仍然是個原始類型呢?

答案很簡單、很無厘頭、很蒼白——爲了版本兼容!

引入泛型的時候,Java 已經進入到第二個十年(年紀大了),市面上存在大量沒有使用 Java 泛型的代碼。若是由於版本升級致使它們不能使用,恐怕 Java 也活不到如今,畢竟對用戶友好纔是一個軟件存在的硬道理。

固然了,Java 已經對開發者作出了警示:強烈建議不要在 Java 代碼中使用原始類型,將來的版本中能夠會禁止使用原始類型,請當心點。

0五、鳴謝

好了各位讀者朋友們,以上就是本文的所有內容了。能看到這裏的都是最優秀的程序員,二哥必需要動動手爲你點個贊👍。若是以爲不過癮,還想看到更多,我再推薦幾篇給你們。

50W+程序員關注過的問題:爲何會發生ArrayIndexOutOfBoundsException?
188W+程序員關注過的問題:Java 究竟是值傳遞仍是引用傳遞?
250W+程序員關注過的問題:什麼是 NullPointerException?
370W+程序員關注過的問題:如何比較 Java 的字符串?

有收穫?就點贊、留言,讓更多的人看到這篇文章。

若是想要第一時間看到我更新的文章,能夠微信搜索「沉默王二」,關注個人公衆號,回覆「java」再送你一份精選電子書大禮包,包含這十年來我讀過的最優質的 Java 書籍。

相關文章
相關標籤/搜索