您好,登錄后才能下訂單哦!
請求超時!
本篇文章為大家展示了Java常見知識點中的泛型指的是什么,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
開發人員在使用泛型的時候,很容易根據自己的直覺而犯一些錯誤。比如一個方法如果接收List作為形式參數,那么如果嘗試將一個List的對象作為實際參數傳進去,卻發現無法通過編譯。雖然從直覺上來說,Object是String的父類,這種類型轉換應該是合理的。但是實際上這會產生隱含的類型轉換問題,因此編譯器直接就禁止這樣的行為。一. 類型擦除Java中的泛型基本上都是在編譯器這個層次來實現的,在生成的Java字節代碼中是不包含泛型中的類型信息的。使用泛型的時候加上的類型參數,會被編譯器在編譯的時候去掉,這個過程就稱為類型擦除。如在代碼中定義的List和List等類型,在編譯之后都會變成List。JVM看到的只是List,而由泛型附加的類型信息對JVM來說是不可見的。Java編譯器會在編譯時盡可能的發現可能出錯的地方,但是仍然無法避免在運行時刻出現類型轉換異常的情況。很多泛型的奇怪特性都與這個類型擦除的存在有關,包括:泛型類并沒有自己獨有的Class類對象。比如并不存在List.class或是List.class,而只有List.class。靜態變量是被泛型類的所有實例所共享的。對于聲明為MyClass的類,訪問其中的靜態變量的方法仍然是 MyClass.myStaticVar。不管是通過new MyClass還是new MyClass創建的對象,都是共享一個靜態變量。泛型的類型參數不能用在Java異常處理的catch語句中。因為異常處理是由JVM在運行時刻來進行的。由于類型信息被擦除,JVM是無法區分兩個異常類型MyException和MyException的。對于JVM來說,它們都是 MyException類型的。也就無法執行與異常對應的catch語句。類型擦除的基本過程也比較簡單,首先是找到用來替換類型參數的具體類。這個具體類一般是Object。如果指定了類型參數的上界的話,則使用這個上界。把代碼中的類型參數都替換成具體的類。同時去掉出現的類型聲明,即去掉<>的內容。比如T get()方法聲明就變成了Object get();List就變成了List。接下來就可能需要生成一些橋接方法(bridge method)。這是由于擦除了類型之后的類可能缺少某些必須的方法。比如考慮下面的代碼:class MyString implements Comparable<String> { public int compareTo(String str) { return 0; } }當類型信息被擦除之后,上述類的聲明變成了class MyString implements Comparable。但是這樣的話,類MyString就會有編譯錯誤,因為沒有實現接口Comparable聲明的int compareTo(Object)方法。這個時候就由編譯器來動態生成這個方法。二. 通配符在使用泛型類的時候,既可以指定一個具體的類型,如List就聲明了具體的類型是String;也可以用通配符?來表示未知類型,如List<?>就聲明了List中包含的元素類型是未知的。 通配符所代表的其實是一組類型,但具體的類型是未知的。List<?>所聲明的就是所有類型都是可以的。但是List<?>并不等同于List。List實際上確定了List中包含的是Object及其子類,在使用的時候都可以通過Object來進行引用。而List<?>其中所包含的元素類型是不確定。其中可能包含的是String,也可能是 Integer。如果它包含了String的話,往里面添加Integer類型的元素就是錯誤的。正因為類型未知,就不能通過new ArrayList()的方法來創建一個新的ArrayList對象。因為編譯器無法知道具體的類型是什么。但是對于 List中的元素確總是可以用Object來引用的,因為雖然類型未知,但肯定是Object及其子類。考慮下面的代碼:public void wildcard(List<?> list) { list.add(1);//編譯錯誤 }如上所示,試圖對一個帶通配符的泛型類進行操作的時候,總是會出現編譯錯誤。其原因在于通配符所表示的類型是未知的。因為對于List<?>中的元素只能用Object來引用,在有些情況下不是很方便。在這些情況下,可以使用上下界來限制未知類型的范圍。 如 List<? extends Number>說明List中可能包含的元素類型是Number及其子類。而List<? super Number>則說明List中包含的是Number及其父類。當引入了上界之后,在使用類型的時候就可以使用上界類中定義的方法。三. 類型系統在Java中,大家比較熟悉的是通過繼承機制而產生的類型體系結構。比如String繼承自Object。根據Liskov替換原則,子類是可以替換父類的。當需要Object類的引用的時候,如果傳入一個String對象是沒有任何問題的。但是反過來的話,即用父類的引用替換子類引用的時候,就需要進行強制類型轉換。編譯器并不能保證運行時刻這種轉換一定是合法的。這種自動的子類替換父類的類型轉換機制,對于數組也是適用的。 String[]可以替換Object[]。但是泛型的引入,對于這個類型系統產生了一定的影響。正如前面提到的List是不能替換掉List的。引入泛型之后的類型系統增加了兩個維度:一個是類型參數自身的繼承體系結構,另外一個是泛型類或接口自身的繼承體系結構。第一個指的是對于 List和List這樣的情況,類型參數String是繼承自Object的。而第二種指的是 List接口繼承自Collection接口。對于這個類型系統,有如下的一些規則:相同類型參數的泛型類的關系取決于泛型類自身的繼承體系結構。即List是Collection的子類型,List可以替換Collection。這種情況也適用于帶有上下界的類型聲明。當泛型類的類型聲明中使用了通配符的時候,其子類型可以在兩個維度上分別展開。如對Collection<? extends Number>來說,其子類型可以在Collection這個維度上展開,即List<? extends Number>和Set<? extends Number>等;也可以在Number這個層次上展開,即Collection和Collection等。如此循環下去,ArrayList和 HashSet等也都算是Collection<? extends Number>的子類型。如果泛型類中包含多個類型參數,則對于每個類型參數分別應用上面的規則。
Java中的泛型基本上都是在編譯器這個層次來實現的,在生成的Java字節代碼中是不包含泛型中的類型信息的。使用泛型的時候加上的類型參數,會被編譯器在編譯的時候去掉,這個過程就稱為類型擦除。如在代碼中定義的List和List等類型,在編譯之后都會變成List。JVM看到的只是List,而由泛型附加的類型信息對JVM來說是不可見的。Java編譯器會在編譯時盡可能的發現可能出錯的地方,但是仍然無法避免在運行時刻出現類型轉換異常的情況。很多泛型的奇怪特性都與這個類型擦除的存在有關,包括:泛型類并沒有自己獨有的Class類對象。比如并不存在List.class或是List.class,而只有List.class。靜態變量是被泛型類的所有實例所共享的。對于聲明為MyClass的類,訪問其中的靜態變量的方法仍然是 MyClass.myStaticVar。不管是通過new MyClass還是new MyClass創建的對象,都是共享一個靜態變量。泛型的類型參數不能用在Java異常處理的catch語句中。因為異常處理是由JVM在運行時刻來進行的。由于類型信息被擦除,JVM是無法區分兩個異常類型MyException和MyException的。對于JVM來說,它們都是 MyException類型的。也就無法執行與異常對應的catch語句。類型擦除的基本過程也比較簡單,首先是找到用來替換類型參數的具體類。這個具體類一般是Object。如果指定了類型參數的上界的話,則使用這個上界。把代碼中的類型參數都替換成具體的類。同時去掉出現的類型聲明,即去掉<>的內容。比如T get()方法聲明就變成了Object get();List就變成了List。接下來就可能需要生成一些橋接方法(bridge method)。這是由于擦除了類型之后的類可能缺少某些必須的方法。比如考慮下面的代碼:class MyString implements Comparable<String> { public int compareTo(String str) { return 0; } }當類型信息被擦除之后,上述類的聲明變成了class MyString implements Comparable。但是這樣的話,類MyString就會有編譯錯誤,因為沒有實現接口Comparable聲明的int compareTo(Object)方法。這個時候就由編譯器來動態生成這個方法。二. 通配符在使用泛型類的時候,既可以指定一個具體的類型,如List就聲明了具體的類型是String;也可以用通配符?來表示未知類型,如List<?>就聲明了List中包含的元素類型是未知的。 通配符所代表的其實是一組類型,但具體的類型是未知的。List<?>所聲明的就是所有類型都是可以的。但是List<?>并不等同于List。List實際上確定了List中包含的是Object及其子類,在使用的時候都可以通過Object來進行引用。而List<?>其中所包含的元素類型是不確定。其中可能包含的是String,也可能是 Integer。如果它包含了String的話,往里面添加Integer類型的元素就是錯誤的。正因為類型未知,就不能通過new ArrayList()的方法來創建一個新的ArrayList對象。因為編譯器無法知道具體的類型是什么。但是對于 List中的元素確總是可以用Object來引用的,因為雖然類型未知,但肯定是Object及其子類。考慮下面的代碼:public void wildcard(List<?> list) { list.add(1);//編譯錯誤 }如上所示,試圖對一個帶通配符的泛型類進行操作的時候,總是會出現編譯錯誤。其原因在于通配符所表示的類型是未知的。因為對于List<?>中的元素只能用Object來引用,在有些情況下不是很方便。在這些情況下,可以使用上下界來限制未知類型的范圍。 如 List<? extends Number>說明List中可能包含的元素類型是Number及其子類。而List<? super Number>則說明List中包含的是Number及其父類。當引入了上界之后,在使用類型的時候就可以使用上界類中定義的方法。三. 類型系統在Java中,大家比較熟悉的是通過繼承機制而產生的類型體系結構。比如String繼承自Object。根據Liskov替換原則,子類是可以替換父類的。當需要Object類的引用的時候,如果傳入一個String對象是沒有任何問題的。但是反過來的話,即用父類的引用替換子類引用的時候,就需要進行強制類型轉換。編譯器并不能保證運行時刻這種轉換一定是合法的。這種自動的子類替換父類的類型轉換機制,對于數組也是適用的。 String[]可以替換Object[]。但是泛型的引入,對于這個類型系統產生了一定的影響。正如前面提到的List是不能替換掉List的。引入泛型之后的類型系統增加了兩個維度:一個是類型參數自身的繼承體系結構,另外一個是泛型類或接口自身的繼承體系結構。第一個指的是對于 List和List這樣的情況,類型參數String是繼承自Object的。而第二種指的是 List接口繼承自Collection接口。對于這個類型系統,有如下的一些規則:相同類型參數的泛型類的關系取決于泛型類自身的繼承體系結構。即List是Collection的子類型,List可以替換Collection。這種情況也適用于帶有上下界的類型聲明。當泛型類的類型聲明中使用了通配符的時候,其子類型可以在兩個維度上分別展開。如對Collection<? extends Number>來說,其子類型可以在Collection這個維度上展開,即List<? extends Number>和Set<? extends Number>等;也可以在Number這個層次上展開,即Collection和Collection等。如此循環下去,ArrayList和 HashSet等也都算是Collection<? extends Number>的子類型。如果泛型類中包含多個類型參數,則對于每個類型參數分別應用上面的規則。
很多泛型的奇怪特性都與這個類型擦除的存在有關,包括:
泛型類并沒有自己獨有的Class類對象。比如并不存在List
靜態變量是被泛型類的所有實例所共享的。對于聲明為MyClass
泛型的類型參數不能用在Java異常處理的catch語句中。因為異常處理是由JVM在運行時刻來進行的。由于類型信息被擦除,JVM是無法區分兩個異常類型MyException
類型擦除的基本過程也比較簡單,首先是找到用來替換類型參數的具體類。這個具體類一般是Object。如果指定了類型參數的上界的話,則使用這個上界。把代碼中的類型參數都替換成具體的類。同時去掉出現的類型聲明,即去掉<>的內容。比如T get()方法聲明就變成了Object get();List就變成了List。接下來就可能需要生成一些橋接方法(bridge method)。這是由于擦除了類型之后的類可能缺少某些必須的方法。比如考慮下面的代碼:
class MyString implements Comparable<String> { public int compareTo(String str) { return 0; } }
當類型信息被擦除之后,上述類的聲明變成了class MyString implements Comparable。但是這樣的話,類MyString就會有編譯錯誤,因為沒有實現接口Comparable聲明的int compareTo(Object)方法。這個時候就由編譯器來動態生成這個方法。
在使用泛型類的時候,既可以指定一個具體的類型,如List就聲明了具體的類型是String;也可以用通配符?來表示未知類型,如List<?>就聲明了List中包含的元素類型是未知的。 通配符所代表的其實是一組類型,但具體的類型是未知的。List<?>所聲明的就是所有類型都是可以的。但是List<?>并不等同于List。List實際上確定了List中包含的是Object及其子類,在使用的時候都可以通過Object來進行引用。而List<?>其中所包含的元素類型是不確定。其中可能包含的是String,也可能是 Integer。如果它包含了String的話,往里面添加Integer類型的元素就是錯誤的。正因為類型未知,就不能通過new ArrayList()的方法來創建一個新的ArrayList對象。因為編譯器無法知道具體的類型是什么。但是對于 List中的元素確總是可以用Object來引用的,因為雖然類型未知,但肯定是Object及其子類。考慮下面的代碼:
public void wildcard(List<?> list) { list.add(1);//編譯錯誤 }
如上所示,試圖對一個帶通配符的泛型類進行操作的時候,總是會出現編譯錯誤。其原因在于通配符所表示的類型是未知的。
因為對于List<?>中的元素只能用Object來引用,在有些情況下不是很方便。在這些情況下,可以使用上下界來限制未知類型的范圍。 如 List<? extends Number>說明List中可能包含的元素類型是Number及其子類。而List<? super Number>則說明List中包含的是Number及其父類。當引入了上界之后,在使用類型的時候就可以使用上界類中定義的方法。
在Java中,大家比較熟悉的是通過繼承機制而產生的類型體系結構。比如String繼承自Object。根據Liskov替換原則,子類是可以替換父類的。當需要Object類的引用的時候,如果傳入一個String對象是沒有任何問題的。但是反過來的話,即用父類的引用替換子類引用的時候,就需要進行強制類型轉換。編譯器并不能保證運行時刻這種轉換一定是合法的。這種自動的子類替換父類的類型轉換機制,對于數組也是適用的。 String[]可以替換Object[]。但是泛型的引入,對于這個類型系統產生了一定的影響。正如前面提到的List是不能替換掉List的。
引入泛型之后的類型系統增加了兩個維度:一個是類型參數自身的繼承體系結構,另外一個是泛型類或接口自身的繼承體系結構。第一個指的是對于 List和List這樣的情況,類型參數String是繼承自Object的。而第二種指的是 List接口繼承自Collection接口。對于這個類型系統,有如下的一些規則:
相同類型參數的泛型類的關系取決于泛型類自身的繼承體系結構。即List
當泛型類的類型聲明中使用了通配符的時候,其子類型可以在兩個維度上分別展開。如對Collection<? extends Number>來說,其子類型可以在Collection這個維度上展開,即List<? extends Number>和Set<? extends Number>等;也可以在Number這個層次上展開,即Collection
如果泛型類中包含多個類型參數,則對于每個類型參數分別應用上面的規則。
上述內容就是Java常見知識點中的泛型指的是什么,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。
億速云公眾號
手機網站二維碼
Copyright ? Yisu Cloud Ltd. All Rights Reserved. 2018 版權所有
廣州億速云計算有限公司粵ICP備17096448號-1 粵公網安備 44010402001142號增值電信業務經營許可證編號:B1-20181529