成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

關于Java泛型這些事

開發 后端
泛型(generics)是 JDK 5 中引入的一個新特性, 泛型提供了編譯時類型安全檢測機制,該機制允許程序員在編譯時檢測到非法的類型。

[[443125]]

關于泛型,有一道經典的考題:

  1. public static void main(String[] args) { 
  2.         List<String> list1= new ArrayList<String>(); 
  3.         List<Integer> list2= new ArrayList<Integer>(); 
  4.         System.out.println(list1.getClass() == list2.getClass()); 
  5.     } 

請問上面代碼的輸出結果是什么?

如果是了解泛型的同學會很容易答出:true,如果是不了解泛型的同學則很可能會答錯。今天就和大家一起來重溫一下Java泛型相關的知識。

一、什么是泛型?

泛型(generics)是 JDK 5 中引入的一個新特性, 泛型提供了編譯時類型安全檢測機制,該機制允許程序員在編譯時檢測到非法的類型。泛型的本質是參數化類型,也就是說所操作的數據類型被指定為一個參數。具有以下特點:

  • 與普通的 Object 代替一切類型這樣簡單粗暴而言,泛型使得數據的類別可以像參數一樣由外部傳遞進來。它提供了一種擴展能力。它更符合面向抽象開發的軟件編程宗旨。
  • 當具體的類型確定后,泛型又提供了一種類型檢測的機制,只有相匹配的數據才能正常的賦值,否則編譯器就不通過。所以說,它是一種類型安全檢測機制,一定程度上提高了軟件的安全性防止出現低級的失誤。
  • 泛型提高了程序代碼的可讀性,不必要等到運行的時候才去強制轉換,在定義或者實例化階段,因為 Cache這個類型顯化的效果,程序員能夠一目了然猜測出代碼要操作的數據類型。

泛型按照使用情況可以分為3種:泛型類、泛型方法、泛型接口。

1.泛型類

我們可以定義如下一個泛型類

  1. /** 
  2.  * @author machongjia 
  3.  * @date 2021/12/28 20:02 
  4.  * @description 
  5.  */ 
  6. public class Generic<T> { 
  7.  
  8.     private T var; 
  9.  
  10.     public Generic(T var) { 
  11.         this.var = var; 
  12.     } 
  13.  
  14.     public T getVar() { 
  15.         return var; 
  16.     } 
  17.  
  18.     public static void main(String[] args) { 
  19.         Generic<Integer> i = new Generic<Integer>(1000); 
  20.         Generic<String> s = new Generic<String>("hello"); 
  21.         System.out.println(i.getVar()); 
  22.         System.out.println(s.getVar()); 
  23.     } 

輸出結果:

  1. 1000 
  2. hello 

常用的類似于T這樣的類型參數包括:

T:代表一般的任何類

E:代表 Element 的意思,或者 Exception 異常的意思

K:代表 Key 的意思。

V:代表 Value 的意思,通常與 K 一起配合使用

S:代表 Subtype 的意思

泛型類可以不止接受一個參數T,還可以接受多個參數,類似于下面這種:

  1. public class Generic<E,T> { 
  2.  
  3.     private E var1; 
  4.     private T var2; 
  5.  
  6.     public Generic(E var1, T var2) { 
  7.         this.var1 = var1; 
  8.         this.var2 = var2; 
  9.     } 
  10.  
  11.     public static void main(String[] args) { 
  12.         Generic<Integer,String> generic = new Generic<Integer,String>(1000,"hello"); 
  13.         System.out.println(generic.var1); 
  14.         System.out.println(generic.var2); 
  15.     } 

2.泛型方法

  1. public class Generic { 
  2.  
  3.     public <T> void testMethod(T t){ 
  4.  
  5.     } 
  6.  

泛型方法與泛型類稍有不同的地方是,類型參數也就是尖括號那一部分是寫在返回值前面的。中的 T 被稱為類型參數,而方法中的 T 被稱為參數化類型,它不是運行時真正的參數。

當然,聲明的類型參數,其實也是可以當作返回值的類型的。

泛型類與泛型方法共存的情況:

  1. public class Generic<T> { 
  2.  
  3.     public void testMethod(T t){ 
  4.         System.out.println(t.getClass().getName()); 
  5.     } 
  6.  
  7.     public <T> T testMethod1(T t){ 
  8.         return t; 
  9.     } 

上面代碼中,Test1是泛型類,testMethod 是泛型類中的普通方法,而 testMethod1 是一個泛型方法。而泛型類中的類型參數與泛型方法中的類型參數是沒有相應的聯系的,泛型方法始終以自己定義的類型參數為準。

3.泛型接口

泛型接口與泛型類的定義及使用基本相同。泛型接口常被用在各種類的生產器中,可以看一個例子:

  1. //定義一個泛型接口 
  2. public interface Generator<T> { 
  3.     public T next(); 

當實現泛型接口的類,未傳入泛型實參時:

  1. /** 
  2.  * 未傳入泛型實參時,與泛型類的定義相同,在聲明類的時候,需將泛型的聲明也一起加到類中 
  3.  * 即:class FruitGenerator<T> implements Generator<T>{ 
  4.  * 如果不聲明泛型,如:class FruitGenerator implements Generator<T>,編譯器會報錯:"Unknown class" 
  5.  */ 
  6. class FruitGenerator<T> implements Generator<T>{ 
  7.     @Override 
  8.     public T next() { 
  9.         return null
  10.     } 

當實現泛型接口的類,傳入泛型實參時:

  1. /** 
  2.  * 傳入泛型實參時,定義一個生產器實現這個接口,雖然我們只創建了一個泛型接口Generator<T> 
  3.  * 但是我們可以為T傳入無數個實參,形成無數種類型的Generator接口。 
  4.  * 在實現類實現泛型接口時,如已將泛型類型傳入實參類型,則所有使用泛型的地方都要替換成傳入的實參類型 
  5.  * 即:Generator<T>,public T next();中的的T都要替換成傳入的String類型。 
  6.  */ 
  7. public class FruitGenerator implements Generator<String> { 
  8.  
  9.     private String[] fruits = new String[]{"Apple""Banana""Pear"}; 
  10.  
  11.     @Override 
  12.     public String next() { 
  13.         Random rand = new Random(); 
  14.         return fruits[rand.nextInt(3)]; 
  15.     } 

4.通配符?

通配符的出現是為了指定泛型中的類型范圍,包含以下3 種形式。

  • <?>被稱作無限定的通配符。
  • <? extends T>被稱作有上限的通配符。
  • <? super T>被稱作有下限的通配符。

無限定通配符<?>

無限定通配符經常與容器類配合使用,它其中的 ? 其實代表的是未知類型,所以涉及到 ? 時的操作,一定與具體類型無關。

  1. public void testWildCards(Collection<?> collection){ 

上面的代碼中,方法內的參數是被無限定通配符修飾的 Collection 對象,它隱略地表達了一個意圖或者可以說是限定,那就是 testWidlCards() 這個方法內部無需關注 Collection 中的真實類型,因為它是未知的。所以,你只能調用 Collection 中與類型無關的方法。

相對應,前者?代表類型T及T的子類,后者?代表T及T的超類。

值得注意的是,如果用泛型方法來取代通配符,那么上面代碼中 collection 是能夠進行寫操作的。只不過要進行強制轉換。

二、什么是泛型的類型擦除?

Java泛型這個特性是從JDK 1.5才開始加入的,因此為了兼容之前的版本,Java泛型的實現采取了“偽泛型”的策略,即Java在語法上支持泛型,但是在編譯階段會進行所謂的“類型擦除”(Type Erasure),將所有的泛型表示(尖括號中的內容)都替換為具體的類型(其對應的原生態類型),就像完全沒有泛型一樣。理解類型擦除對于用好泛型是很有幫助的,尤其是一些看起來“疑難雜癥”的問題,弄明白了類型擦除也就迎刃而解了。

  • 泛型的類型擦除原則是:
  • 消除類型參數聲明,即刪除<>及其包圍的部分。
  • 根據類型參數的上下界推斷并替換所有的類型參數為原生態類型:如果類型參數是無限制通配符或沒有上下界限定則替換為Object,如果存在上下界限定則根據子類替換原則取類型參數的最左邊限定類型(即父類)。
  • 為了保證類型安全,必要時插入強制類型轉換代碼。
  • 自動產生“橋接方法”以保證擦除類型后的代碼仍然具有泛型的“多態性”。

1.類型擦除做了什么?

上面我們說了,編譯完成后會對泛型進行類型擦除,如果想要眼見為實,實際看一下的話應該怎么辦呢?那么就需要對編譯后的字節碼文件進行反編譯了,這里使用一個輕量級的小工具Jad來進行反編譯,Jad的使用也很簡單,下載解壓后,把需要反編譯的字節碼文件放在目錄下,然后在命令行里執行下面的命令就可以在同目錄下生成反編譯后的.java文件了:

  1. jad -sjava Test.class 

好了,工具準備好了,下面我們就看一下不同情況下的類型擦除。

無限制類型擦除

當類定義中的類型參數沒有任何限制時,在類型擦除后,會被直接替換為Object。在下面的例子中,中的類型參數T就全被替換為了Object(左側為編譯前的代碼,右側為通過字節碼文件反編譯得到的代碼):

有限制類型擦除 

當類定義中的類型參數存在限制時,在類型擦除中替換為類型參數的上界或者下界。下面的代碼中,經過擦除后T被替換成了Integer:

擦除方法中的類型參數

比較下面兩邊的代碼,可以看到在擦除方法中的類型參數時,和擦除類定義中的類型參數一致,無限制時直接擦除為Object,有限制時則會被擦除為上界或下界:

2.類型擦除帶來了哪些局限性?

類型擦除,是泛型能夠與之前的 java 版本代碼兼容共存的原因。但也因為類型擦除,它會抹掉很多繼承相關的特性,這是它帶來的局限性。

理解類型擦除有利于我們繞過開發當中可能遇到的雷區,同樣理解類型擦除也能讓我們繞過泛型本身的一些限制。比如

正常情況下,因為泛型的限制,編譯器不讓最后一行代碼編譯通過,因為類似不匹配,但是,基于對類型擦除的了解,利用反射,我們可以繞過這個限制。

  1. public interface List<E> extends Collection<E>{ 
  2.    
  3.    boolean add(E e); 

上面是 List 和其中的 add() 方法的源碼定義。

因為 E 代表任意的類型,所以類型擦除時,add 方法其實等同于:

  1. boolean add(Object obj); 

那么,利用反射,我們繞過編譯器去調用 add 方法。

  1. public class ToolTest { 
  2.  
  3.   public static void main(String[] args) { 
  4.     List<Integer> ls = new ArrayList<>(); 
  5.     ls.add(23); 
  6. //    ls.add("text"); 
  7.     try { 
  8.       Method method = ls.getClass().getDeclaredMethod("add",Object.class); 
  9.  
  10.       method.invoke(ls,"test"); 
  11.       method.invoke(ls,42.9f); 
  12.     } catch (NoSuchMethodException e) { 
  13.       // TODO Auto-generated catch block 
  14.       e.printStackTrace(); 
  15.     } catch (SecurityException e) { 
  16.       // TODO Auto-generated catch block 
  17.       e.printStackTrace(); 
  18.     } catch (IllegalAccessException e) { 
  19.       // TODO Auto-generated catch block 
  20.       e.printStackTrace(); 
  21.     } catch (IllegalArgumentException e) { 
  22.       // TODO Auto-generated catch block 
  23.       e.printStackTrace(); 
  24.     } catch (InvocationTargetException e) { 
  25.       // TODO Auto-generated catch block 
  26.       e.printStackTrace(); 
  27.     } 
  28.  
  29.     for ( Object o: ls){ 
  30.       System.out.println(o); 
  31.     } 
  32.   } 

打印結果是:

  1. 23 
  2. test 
  3. 42.9 

可以看到,利用類型擦除的原理,用反射的手段就繞過了正常開發中編譯器不允許的操作限制。

 

責任編輯:武曉燕 來源: 碼蟲甲
相關推薦

2021-07-29 09:20:18

Java泛型String

2017-03-07 11:45:57

DevOps容器

2017-04-10 15:11:16

2021-09-28 06:28:50

云原生安全云計算

2018-01-03 11:35:34

推送AndroidiOS

2024-04-23 08:23:36

TypeScript泛型Generics

2009-09-25 10:03:51

Java泛型

2022-07-12 06:17:43

GoogleGolang開發工作

2009-09-01 17:59:36

C#泛型的作用

2015-07-13 08:49:54

2017-10-12 10:20:13

服務器運行壽命

2021-06-17 06:51:32

Java泛型Java編程

2011-06-03 08:49:54

Java

2017-03-06 16:51:52

Java泛型實現

2020-10-20 10:17:20

Java泛型Type

2019-06-25 15:00:53

SpringNullJava

2021-09-29 18:17:30

Go泛型語言

2021-03-26 08:41:11

Go語言Docker

2021-07-01 06:47:30

Java泛型泛型擦除

2012-05-01 08:06:49

手機
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产一区二区三区四区在线观看 | 久久国产免费看 | 午夜视频一区 | 国产精品久久久久久久久久免费看 | 激情91| 97热在线 | 欧美日韩一区在线 | 欧美亚州 | 在线视频国产一区 | 欧美日本在线 | 福利影院在线看 | 国产欧美日韩在线一区 | 国产一区二区精品在线观看 | 国产一级视频在线播放 | www.久久国产精品 | 亚洲精品无人区 | 国产一区高清 | 日韩一区不卡 | 91精品国产777在线观看 | 精品欧美一区二区三区久久久 | 日韩在线观看中文字幕 | 欧美日韩中文在线 | 天天综合网天天综合色 | 国产精品久久久久久久久图文区 | 中国一级特黄真人毛片免费观看 | 亚洲精品乱码久久久久久蜜桃91 | 日韩一区二区三区在线 | 国产香蕉视频在线播放 | 色综合久久久 | 四虎影音 | 男女国产网站 | 国产欧美一级 | 欧洲一区二区三区 | 久久久久久久av | 国产成人精品一区二区三区视频 | 国产在线精品一区二区 | 一区福利视频 | 欧美aⅴ| 97精品超碰一区二区三区 | 日日夜夜天天 | 久久久女女女女999久久 |