1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時(shí)間:8:30-17:00
      你可能遇到了下面的問題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
      什么是Java的泛型

      這篇文章主要介紹“什么是Java的泛型”,在日常操作中,相信很多人在什么是Java的泛型問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對(duì)大家解答”什么是Java的泛型”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

      公司主營業(yè)務(wù):成都網(wǎng)站建設(shè)、網(wǎng)站制作、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)公司推出遼源免費(fèi)做網(wǎng)站回饋大家。

      最近技術(shù)交流群里,有朋友問:Object和泛型T有啥區(qū)別。回答完問題,不禁在想,面試在即,還有那么多朋友不了泛型?是時(shí)候給大家整理一篇泛型相關(guān)的文章了,一篇文章全面搞定泛型,讓大家再也不愁面試或?qū)嵺`中泛型相關(guān)的問題了。

      什么是泛型

      泛型是在JDK 5時(shí)就引入的新特性,也就是“參數(shù)化類型”,通俗來講就是將原來的具體類型通過參數(shù)化來定義,使用或調(diào)用時(shí)再傳入具體的類型(類型實(shí)參)。

      泛型的本質(zhì)是為了參數(shù)化類型(在不創(chuàng)建新類型的前提下,通過泛型指定的不同類型來控制形參具體的類型)。在泛型使用過程中,操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù),這種參數(shù)類型可以用在類、接口和方法中,分別被稱為泛型類、泛型接口、泛型方法。

      為什么使用泛型

      未使用泛型時(shí),可以通過Object來實(shí)現(xiàn)參數(shù)的“任意化”,但這樣做的缺點(diǎn)就是需要顯式的強(qiáng)制類型轉(zhuǎn)換,這就需要開發(fā)者知道實(shí)際的類型。

      而強(qiáng)制類型轉(zhuǎn)換是會(huì)出現(xiàn)錯(cuò)誤的,比如Object將實(shí)際類型為String,強(qiáng)轉(zhuǎn)成Integer。編譯期是不會(huì)提示錯(cuò)誤的,而在運(yùn)行時(shí)就會(huì)拋出異常,很明顯的安全隱患。

      Java通過引入泛型機(jī)制,將上述的隱患提前到編譯期進(jìn)行檢查,開發(fā)人員既可明確的知道實(shí)際類型,又可以通過編譯期的檢查提示錯(cuò)誤,從而提升代碼的安全性和健壯性。

      使用泛型前后的對(duì)比

      拿一個(gè)經(jīng)典的例子來演示一下未使用泛型會(huì)出現(xiàn)的問題。

      List list = new ArrayList(); list.add(1); list.add("zhuan2quan"); list.add("程序新視界");  for (int i = 0; i < list.size(); i++) {     String value = (String) list.get(i);     System.out.println("value=" + value); }

      上述代碼在編譯器并不會(huì)報(bào)任何錯(cuò)誤,但當(dāng)執(zhí)行時(shí)會(huì)拋出如下異常:

      java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

      那么,是否可以在編譯器就解決這個(gè)問題,而不是在運(yùn)行期拋出異常呢?泛型應(yīng)運(yùn)而生。上述代碼通過泛型來寫之后,變成如下形式:

      List list = new ArrayList<>(); list.add(1); list.add("zhuan2quan"); list.add("程序新視界");  for (String value : list) {     System.out.println("value=" + value); }

      可以看出,代碼變得更加清爽簡單,而且list.add(1)這行代碼在IDE中直接會(huì)提示錯(cuò)誤信息:

      Required type: String Provided: int

      提示錯(cuò)誤信息便是泛型對(duì)向List中添加的數(shù)據(jù)產(chǎn)生了約束,只能是String類型。

      泛型中通配符

      在使用泛型時(shí)經(jīng)常會(huì)看到T、E、K、V這些通配符,它們代表著什么含義呢?

      本質(zhì)上它們都是通配符,并沒有什么區(qū)別,換成A-Z之間的任何字母都可以。不過在開發(fā)者之間倒是有些不成文的約定:

      • T (type) 表示具體的一個(gè)java類型;

      • K V (key value) 分別代表java鍵值中的Key Value;

      • E (element) 代表Element;

      為什么Java的泛型是假泛型

      為了做到向下兼容,Java中的泛型僅僅是一個(gè)語法糖,并不是C++那樣的真泛型。

      還是上面的例子,在直接向泛型為String的List中添加int類型會(huì)提示錯(cuò)誤:

      List list = new ArrayList<>(); list.add(1);

      針對(duì)上述代碼,我們采用反射間接地調(diào)用add方法:

      @Test public void test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {     List list = new ArrayList<>();     list.add(1);     Method add = list.getClass().getMethod("add", Object.class);     add.invoke(list,"程序新視界");     System.out.println(list);     System.out.println(list.get(1)); }

      執(zhí)行上述代碼,我們發(fā)現(xiàn)程序并沒有拋出異常,正常打印出入:

      [1, 程序新視界] 程序新視界

      原本只能裝入Integer的List,成功裝入了一個(gè)String類型的值。由此可見,所謂的泛型確實(shí)是假泛型。

      同時(shí),我們還可以通過字節(jié)碼來證明。拿上面使用了泛型的實(shí)例代碼,通過javap -c命令來看看字節(jié)碼:

      Code:        0: new           #2                  // class java/util/ArrayList        3: dup        4: invokespecial #3                  // Method java/util/ArrayList."":()V        7: astore_1        8: aload_1        9: ldc           #6                  // String zhuan2quan       11: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z       16: pop       17: aload_1       18: ldc           #7                  // String 程序新視界       20: invokeinterface #5,  2            // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z       25: pop       26: aload_1       27: invokeinterface #18,  1           // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator;       32: astore_2       33: aload_2       34: invokeinterface #19,  1           // InterfaceMethod java/util/Iterator.hasNext:()Z       39: ifeq          80

      從字節(jié)碼中可以看出,List.add方法本質(zhì)上就是一個(gè)Object。再次證明,Java的泛型僅僅在編譯期有效,在運(yùn)行期則會(huì)被擦除,也就是說所有的泛型參數(shù)類型在編譯后都會(huì)被清除掉。這就是我們經(jīng)常說的類型擦除。

      因此,也可以說:泛型類型在邏輯上看以看成是多個(gè)不同的類型,實(shí)際上都是相同的基本類型。

      泛型的定義與使用

      泛型有三類,分別為:泛型類、泛型接口、泛型方法。

      在學(xué)習(xí)這三種類型的泛型使用場景之前,我們需要明確一個(gè)基本準(zhǔn)則,那就是泛型的聲明通常都是通過<>配合大寫字母來定義的,比如。只不過不同類型,聲明的位置不同,使用的方式也有所不同。

      泛型類

      泛型類的語法形式:

      class name { /* ... */ }

      泛型類的聲明和非泛型類的聲明類似,只是在類名后面添加了類型參數(shù)聲明部分。由尖括號(hào)(<>)分隔的類型參數(shù)部分跟在類名后面。它指定類型參數(shù)(也稱為類型變量)T1,T2,...和  Tn。一般將泛型中的類名稱為原型,而將<>指定的參數(shù)稱為類型參數(shù)。

      使用示例:

      // T為任意標(biāo)識(shí),比如用T、E、K、V等表示泛型 public class Foo {      // 泛化的成員變量,T的類型由外部指定     private T info;      // 構(gòu)造方法類型為T,T的類型由外部指定     public Foo(T info){         this.info = info;     }      // 方法返回值類型為T,T的類型由外部指定     public T getInfo() {         return info;     }      public static void main(String[] args) {         // 實(shí)例化泛型類時(shí),必須指定T的具體類型,這里為String。         // 傳入的實(shí)參類型需與泛型的類型參數(shù)類型相同,這里為String。         Foo foo = new Foo<>("程序新視界");         System.out.println(foo.getInfo());     } }

      當(dāng)然,上述示例中在使用泛型類時(shí)也可以不指定實(shí)際類型,語法上支持,那么此時(shí)與未定義泛型一樣,不推薦這種方式。

      Foo foo11 = new Foo(1);

      比如上述寫法,也是可行的,但時(shí)區(qū)了定義泛型的意義了。

      泛型接口

      泛型接口的聲明與泛型類一致,泛型接口語法形式:

      public interface Context {     T getContext(); }

      泛型接口有兩種實(shí)現(xiàn)方式:子類明確聲明泛型類型和子類不明確聲明泛型類型。

      先看子類明確聲明泛型類型的示例:

      // 實(shí)現(xiàn)泛型接口時(shí)已傳入實(shí)參類型,則所有使用泛型的地方都要替換成傳入的實(shí)參類型 public class TomcatContext implements Context {     @Override     public String getContext() {         return "Tomcat";     } }

      子類不明確聲明泛型類型:

      // 未傳入泛型實(shí)參時(shí),與泛型類的定義相同,在聲明類的時(shí)候,需將泛型的聲明也一起加到類中 public class SpringContext implements Context{     @Override     public T getContext() {         return null;     } }

      當(dāng)然,還有一種情況,就是我們把定義為泛型的類像前面講的一樣當(dāng)做普通類使用。

      上面的示例中泛型參數(shù)都是一個(gè),當(dāng)然也可以指定兩個(gè)或多個(gè):

      public interface GenericInterfaceSeveralTypes< T, R > {     R performAction( final T action ); }

      多個(gè)泛型參數(shù)可以用逗號(hào)(,)進(jìn)行分割。

      泛型方法

      泛型類是在實(shí)例化類時(shí)指明泛型的具體類型;泛型方法是在調(diào)用方法時(shí)指明泛型的具體類型。泛型方法可以是普通方法、靜態(tài)方法、抽象方法、final修飾的方法以及構(gòu)造方法。

      泛型方法語法形式如下:

      public  T func(T obj) {}

      尖括號(hào)內(nèi)為類型參數(shù)列表,位于方法返回值T或void關(guān)鍵字之前。尖括號(hào)內(nèi)定義的T,可以用在方法的任何地方,比如參數(shù)、方法內(nèi)和返回值。

      protected abstract R performAction( final T action );   static R performActionOn( final Collection< T > action ) {     final R result = ...;     // Implementation here     return result; }

      上述實(shí)例中可以看出泛型方法同樣可以定義多個(gè)泛型類型。

      再看一個(gè)示例代碼:

      public class GenericsMethodDemo1 {      //1、public與返回值中間,聲明此方法的泛型類型。     //2、只有聲明了的方法才是泛型方法,泛型類中的使用了泛型的成員方法并不是泛型方法。     //3、表明該方法將使用泛型類型T,此時(shí)才可以在方法中使用泛型類型T。     //4、T可以為任意標(biāo)識(shí),如T、E、K、V等。     public static  T printClass(T obj) {         System.out.println(obj);         return obj;     }      public static void main(String[] args) {         printClass("abc");         printClass(123);     } }

      需要注意的是,泛型方法與類是否是泛型無關(guān)。另外,靜態(tài)方法無法訪問類上定義的泛型;如果靜態(tài)方法操作的引用數(shù)據(jù)類型不確定的時(shí)候,必須要將泛型定義在方法上。

      上述示例中如果GenericsMethodDemo1定義為GenericsMethodDemo1,則printClass方法是無法直接使用到類上的T的,只能像上面代碼那樣訪問自身定義的T。

      泛型方法與普通方法區(qū)別

      下面,我們對(duì)比一下泛型方法和非泛型方法的區(qū)別:

      // 方法一 public T getKey(){     return key; }  // 方法二 public  T showKeyName(T t){     return t; }

      其中方法一雖然使用了T這個(gè)泛型聲明,但它用的是泛型類中定義的變量,因此這個(gè)方法并不是泛型方法。而像方法二中通過兩個(gè)尖括號(hào)聲明了T,這個(gè)才是真正的泛型方法。

      對(duì)于方法二,還有一種情況,那就是類中也聲明了T,那么該方法參數(shù)的T指的只是此方法的T,而并不是類的T。

      泛型方法與可變參數(shù)

      @SafeVarargs public final  void print(T... args){  for(T t : args){   System.out.println("t=" + t);  } }  public static void main(String[] args) {  GenericDemo2 demo2 = new GenericDemo2();  demo2.print("abc",123); }

      print方法打印出可變參數(shù)args中的結(jié)果,而且可變參數(shù)可以傳遞不同的具體類型。

      打印結(jié)果:

      t=abc t=123

      關(guān)于泛型方法總結(jié)一下就是:如果能使用泛型方法盡量使用泛型方法,這樣能將泛型所需到最需要的范圍內(nèi)。如果使用泛型類,則整個(gè)類都進(jìn)行了泛化處理。

      泛型通配符

      類型通配符一般是使用?代替具體的類型實(shí)參(此處是類型實(shí)參,而不是類型形參)。當(dāng)操作類型時(shí)不需要使用類型的具體功能時(shí),只使用Object類中的功能,那么可以用?通配符來表未知類型。例如List在邏輯上是List、List等所有List<具體類型實(shí)參>的父類。

      /**  * 在使用List作為形參的方法中,不能使用List的實(shí)例傳入,  * 也就是說不能把List看作為List的子類;  */ public static void getNumberData(List data) {     System.out.println("data :" + data.get(0)); }  /**  * 在使用List作為形參的方法中,不能使用List的實(shí)例傳入;  */ public static void getStringData(List data) {     System.out.println("data :" + data.get(0)); }  /**  * 使用類型通配符可以表示同時(shí)是List和List、List的引用類型。  * 類型通配符一般是使用?代替具體的類型實(shí)參,注意此處是類型實(shí)參;  * 和Number、String、Integer一樣都是一種實(shí)際的類型,可以把?看成所有類型的父類。  */ public static void getData(List data) {     System.out.println("data :" + data.get(0)); }

      上述三個(gè)方法中,getNumberData只能傳遞List類型的參數(shù),getStringData只能傳遞List類型的參數(shù)。如果它們都只使用了Object類的功能,則可以通過getData方法的形式進(jìn)行聲明,則同時(shí)支持各種類型。

      上述這種類型的通配符也稱作無界通配符,有兩種應(yīng)用場景:

      • 可以使用Object類中提供的功能來實(shí)現(xiàn)的方法。

      • 使用不依賴于類型參數(shù)的泛型類中的方法。

      在getData中使用了?作為通配符,但在某些場景下,需要對(duì)泛型類型實(shí)參進(jìn)行上下邊界的限制。如:類型實(shí)參只準(zhǔn)傳入某種類型的父類或某種類型的子類。

      /**  * 類型通配符上限通過形如List來定義,如此定義就是通配符泛型值接受Number及其下層子類類型。  */ public static void getUperNumber(List data) {     System.out.println("data :" + data.get(0)); }

      通過extends限制了通配符的上邊界,也就是只接受Number及其子類類型。接口的實(shí)現(xiàn)和類的集成都可以通過extends來表示。

      而這里的Number也可以替換為T,表示該通配符所代表的類型是T類型的子類。

      public static void getData(List data) {     System.out.println("data :" + data.get(0)); }

      與上界通配符示對(duì)照也有下界通配符:

      public static void getData(List data) {     System.out.println("data :" + data.get(0)); }

      下界通配符表示該通配符所代表的類型是T類型的父類。

      泛型的限制

      原始類型(比如:int,long,byte等)無法用于泛型,在使用的過程中需要通過它們的包裝類(比如:Integer, Long,  Byte等)來替代。

      final List< Long > longs = new ArrayList<>(); final Set< Integer > integers = new HashSet<>();

      當(dāng)然,在使用的過程中會(huì)涉及到自動(dòng)拆箱和自動(dòng)裝箱的操作:

      final List< Long > longs = new ArrayList<>(); longs.add( 0L ); // 'long' 包裝為 'Long'   long value = longs.get( 0 ); // 'Long'解包'long'

      泛型的類型推斷

      當(dāng)引入泛型之后,每處用到泛型的地方都需要開發(fā)人員加入對(duì)應(yīng)的泛型類型,比如:

      final Map> map =     new HashMap>();   for(final Map.Entry< String, Collection > entry: map.entrySet()) { }

      為了解決上述問題,在Java7中引入了運(yùn)算符<>,編譯器可以推斷出該運(yùn)算符所代表的原始類型。

      因此,Java7及以后,泛型對(duì)象的創(chuàng)建變?yōu)槿缦滦问剑?/p>

      final Map< String, Collection> map = new HashMap<>();

      到此,關(guān)于“什么是Java的泛型”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!


      網(wǎng)站欄目:什么是Java的泛型
      URL網(wǎng)址:http://ef60e0e.cn/article/jiidsg.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        晋城| 扶沟县| 古丈县| 吉木乃县| 微博| 胶州市| 遂平县| 绍兴市| 富民县| 改则县| 临泉县| 峨边| 长白| 辛集市| 普定县| 铁力市| 盖州市| 来宾市| 施秉县| 台南县| 逊克县| 策勒县| 威信县| 定边县| 巴马| 平江县| 章丘市| 密云县| 嵩明县| 安泽县| 巫溪县| 镇雄县| 大石桥市| 晋州市| 靖远县| 闸北区| 阿拉善右旗| 依安县| 福贡县| 托里县| 治县。|