日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

我理解的 Java 泛型

發布時間:2025/3/17 java 18 豆豆
生活随笔 收集整理的這篇文章主要介紹了 我理解的 Java 泛型 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言

在學習 Spring 的依賴注入時, 被 Google 導流到了 Java Generics FAQs. 這篇文章深入講解了 Java 中泛型相關的方方面面, 閱讀完畢后, 整理了自己的一些理解.

概念與名詞

在進入具體的討論之前, 我們需要先明確幾個名詞的含義.

Generic Type

  • generic type && type parameter
    A generic type is a type with formal type parameters.
    以 interface List<E> {} 為例, List<E> 是 generic type, E 是 type parameter.

  • parameterized type && type argument
    A pameterized type is an instantation of a generic type with type argument.
    以 List<String> stringList; 為例, List<String> 是 parameterized type, 是 List<E> 的一個實例, String 是 type argument.

Wildcard

泛型中將通配符(wildcard)分為三類:

  • ? - unbound, 不做任何限定的通配符.
  • ? extends Number - upper bounded, 限定必須是 Number 或其子類.
  • ? super Integer - lower bounded, 限定必須是 Integer 或其父類.

后兩者也被統稱為 bounded wildcard.
結合通配符, parameterized type 也可以劃分為三類.

conceret type argument generic type ---------------------------> conceret parameterized typeunbound type argument generic type ---------------------------> unbound parameterized typebounded type argument generic type ---------------------------> bounded parameterized type 復制代碼

Raw Type

Raw type 的存在是為了兼容引入泛型之前的版本, 大多數時候你都可以不用考慮它.

Type Erasure

嚴格說來, 泛型只存在于編譯期間, JVM 并不感知泛型. 在編譯時, 編譯器通過 type erasure 來消除 type parameter 和 type argument.
具體的處理方式是:

  • Generic type 中的 type parameter 都會被其上確界(leftmost bound)所代替.
  • Parameterized type 中的 type argument 被直接移除, parameterized type 轉變為對應的 raw type.

何為上確界, 對于 upper bounded type paramter 而言, 是指其公共父類, <? extends Number> 對應的就是 Number.
對于其他類型的 type paramter 而言, 因為 type argument 只能是引用類型(reference type), 而引用類型的公共父類是 Object, 所以其上確界都是 Object.


我們可以從 Java Generics FAQs 的例子中看到具體的轉換過程. 左邊是原始的代碼, 右邊是經過 type erasure 轉換后的結果.

這個例子同時也反應了, 在 type erasure 的過程中, 編譯器可能會按需加入 bridge method 和 type cast.

泛型的類型系統

泛型的引入使得對象之間的繼承關系變得更復雜, 如下這個例子中的一部分就是錯誤的.

public class SuperDemo {public static void main(String args[]) {List<Number> a = new ArrayList<Number>();ArrayList<Number> b = new ArrayList<Integer>();List<? extends Number> c = new ArrayList<Integer>();List<? super Number> d = new ArrayList<Object>();List<? super Integer> e = d;} } 復制代碼

理論上, 泛型相關的繼承關系判斷需要從兩個緯度考慮:

  • generic type 之間是否有繼承關系
  • type argument 之間是否有超集關系

具體而言. 對于 type argument 相同的情況, generic type 之間的繼承關系決定兩個 parameterized type 的父子關系, 所以 List<Number> 是 ArrayList<Number> 的父類.
但 ArrayList<Number> 不是 ArrayList<Integer> 的父類, type argument 不同的情況下, 泛型之間的繼承關系判斷會很復雜. 主要是由于 wildcard 的存在, 導致 type argument 可以代表一類類型, 所以要引入集合中的超集(superset)概念, 即一方所代表的所有類型完全包含在以一方內.
最終的判斷標準是, 在 type argument 不相同的情況下, 如果 type argument 是對方的超集, 而且 generic type 與對方相同或者是對方的父類, 那么當前的 parameterized type 才是對方的父類.

這時候再來回答以下的問題就會比較簡單了:

  • How do instantiations of a generic type relate to instantiations of other generic types that have the same type argument?
  • How do unbounded wildcard instantiations of a generic type relate to other instantiations of the same generic type?
  • How do wildcard instantiations with an upper bound relate to other instantiations of the same generic type?
  • How do wildcard instantiations with a lower bound relate to other instantiations of the same generic type?

泛型的特殊使用姿勢

觀察泛型在異常處理和數組中的使用限制, 思考是什么導致了這些限制, 在一定程度上可以驗證自己之前的理解是否正確.

泛型與異常

Java 的異常處理在運行時生效, 而 type erasure 發生在編譯期間, 所以大多數時候, 泛型在異常處理中并沒有用武之地.

  • Java 不允許任何 Throwable 的子類是泛型. 這主要是由于 type erasure 導致 catch 不能在運行時區分一個 generic type 的不同實例, 所以把 error 或者 exception 定義為泛型不具有任何實際意義.
  • 同樣由于 type erasure, catch 語句也不接受 type parameter.
  • throws 語句可以接受 type parameter, 編譯器在編譯時其會將 type parameter 的替換為具體的異常.
  • 你可以 throw 類型為 type parameter 的異常, 但實際上你基本沒有機會這么做, 因為我們無法新建一個類型為 type parameter 的對象.

泛型與數組

與異常處理類似, 數組在運行時保存了每個元素的類型信息, 所以泛型數組也是一個沒有太大意義的概念. 雖然可以定義一個數據的元素為泛型, 但我們僅能新建元素為 unbound parameterized type 的泛型數組. 具體而言, 下例子中 line 1 和 line 2 合法, 但 line 3 是錯誤的.

List<String>[] a; List<?>[] b = new List<?>[10]; a = new List<String>[10]; // error 復制代碼

究其根本, 是因為數據的組成元素都應該是同一類型的: An array is a container object that holds a fixed number of values of a single type. 而同一 generic type 對應的不同實例實質上并不等價, 但經過 type erasure 后, 并不能在運行時區分出這些.

假如能新建元素為 concerete parameterized type 的數組, 考慮如下案例.

List<String>[] stringLists = new List<String>[10]; stringLists[0] = new List<String>(); stringLists[1] = new List<Integer>();復制代碼


總結

以上是生活随笔為你收集整理的我理解的 Java 泛型的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。