一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - java泛型基本知識和通用方法

java泛型基本知識和通用方法

2021-09-15 13:33蔚藍的海洋 Java教程

這篇文章主要介紹了java泛型基礎知識及通用方法,從以下幾個方面介紹一下java的泛型: 基礎, 泛型關鍵字, 泛型方法, 泛型類和接口,感興趣的可以了解一下

一、泛型簡介

1.引入泛型的目的

了解引入泛型的動機,就先從語法糖開始了解。

語法糖

語法糖(Syntactic Sugar),也稱糖衣語法,是由英國計算機學家Peter.J.Landin發明的一個術語,指在計算機語言中添加的某種語法,這種語法對語言的功能并沒有影響,但是更方便程序員使用。Java中最常用的語法糖主要有泛型、變長參數、條件編譯、自動拆裝箱、內部類等。虛擬機并不支持這些語法,它們在編譯階段就被還原回了簡單的基礎語法結構,這個過程成為解語法糖。

泛型的目的: Java 泛型就是把一種語法糖,通過泛型使得在編譯階段完成一些類型轉換的工作,避免在運行時強制類型轉換而出現ClassCastException,即類型轉換異常。

2.泛型初探

JDK 1.5 時才增加了泛型,并在很大程度上都是方便集合的使用,使其能夠記住其元素的數據類型。

在泛型(Generic type或Generics)出現之前,是這么寫代碼的:

?
1
2
3
4
5
6
7
public static void main(String[] args)
{
    List list = new ArrayList();
    list.add("123");
    list.add("456");
    System.out.println((String)list.get(0));
}

當然這是完全允許的,因為List里面的內容是Object類型的,自然任何對象類型都可以放入、都可以取出,但是這么寫會有兩個問題:

1、當一個對象放入集合時,集合不會記住此對象的類型,當再次從集合中取出此對象時,該對象的編譯類型變成了Object。

2、運行時需要人為地強制轉換類型到具體目標,實際的程序絕不會這么簡單,一個不小心就會出現java.lang.ClassCastException。

所以,泛型出現之后,上面的代碼就改成了大家都熟知的寫法:

?
1
2
3
4
5
6
7
public static void main(String[] args)
{
    List<String> list = new ArrayList<String>();
    list.add("123");
    list.add("456");
    System.out.println(list.get(0));
}

這就是泛型。泛型是對Java語言類型系統的一種擴展,有點類似于C++的模板,可以把類型參數看作是使用參數化類型時指定的類型的一個占位符。引入泛型,是對Java語言一個較大的功能增強,帶來了很多的好處。

3.泛型的好處

①類型安全。類型錯誤現在在編譯期間就被捕獲到了,而不是在運行時當作java.lang.ClassCastException展示出來,將類型檢查從運行時挪到編譯時有助于開發者更容易找到錯誤,并提高程序的可靠性。

②消除了代碼中許多的強制類型轉換,增強了代碼的可讀性。

③為較大的優化帶來了可能。

二、泛型的使用

1.泛型類和泛型接口

下面是JDK 1.5 以后,List接口,以及ArrayList類的代碼片段。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
//定義接口時指定了一個類型形參,該形參名為E
public interface List<E> extends Collection<E> {
   //在該接口里,E可以作為類型使用
   public E get(int index) {}
   public void add(E e) {}
}
//定義類時指定了一個類型形參,該形參名為E
public class ArrayList<E> extends AbstractList<E> implements List<E>{
   //在該類里,E可以作為類型使用
   public void set(E e) {
   .......................
   }
}

這就是泛型的實質:允許在定義接口、類時聲明類型形參,類型形參在整個接口、類體內可當成類型使用,幾乎所有可使用普通類型的地方都可以使用這種類型形參。

下面具體講解泛型類的使用。泛型接口的使用與泛型類幾乎相同,可以比對自行學習。

泛型類

定義一個容器類,存放鍵值對key-value,鍵值對的類型不確定,可以使用泛型來定義,分別指定為K和V

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Container<K, V> {
    private K key;
    private V value;
    public Container(K k, V v) {
        key = k;
        value = v;
    }
    public K getkey() {
        return key;
    }
    public V getValue() {
        return value;
    }
    public void setKey() {
        this.key = key;
    }
    public void setValue() {
        this.value = value;
    }
}

在使用Container類時,只需要指定K,V的具體類型即可,從而創建出邏輯上不同的Container實例,用來存放不同的數據類型。

?
1
2
3
4
5
6
7
8
public static void main(String[] args){
                 Container<String,String> c1=new Container<String ,String>("name","hello");
                 Container<String,Integer> c2=new Container<String,Integer>("age",22);
                 Container<Double,Double> c3=new Container<Double,Double>(1.1,1.3);
                 System.out.println(c1.getKey() + " : " + c1.getValue());     
                 System.out.println(c2.getKey() + " : " + c2.getValue());                                                              
                 System.out.println(c3.getKey() + " : " + c3.getValue());
       }

在JDK 1.7 增加了泛型的“菱形”語法:Java允許在構造器后不需要帶完成的泛型信息,只要給出一對尖括號(<>)即可,Java可以推斷尖括號里應該是什么泛型信息。

如下所示:

?
1
2
Container<String,String> c1=new Container<>("name","hello");
Container<String,Integer> c2=new Container<>("age",22);

泛型類派生子類

當創建了帶泛型聲明的接口、父類之后,可以為該接口創建實現類,或者從該父類派生子類,需要注意:使用這些接口、父類派生子類時不能再包含類型形參,需要傳入具體的類型。錯誤的方式:

?
1
public class A extends Container<K, V>{}

正確的方式:

?
1
public class A extends Container<Integer, String>{}

也可以不指定具體的類型,如下:

?
1
public class A extends Container{}

此時系統會把K,V形參當成Object類型處理。

2.泛型的方法

前面在介紹泛型類和泛型接口中提到,可以在泛型類、泛型接口的方法中,把泛型中聲明的類型形參當成普通類型使用。 如下面的方式:

?
1
2
3
4
5
6
7
8
9
10
public class Container<K, V> {
........................
    public K getkey() {
        return key;
    }
    public void setKey() {
        this.key = key;
    }
....................
}

但在另外一些情況下,在類、接口中沒有使用泛型時,定義方法時想定義類型形參,就會使用泛型方法。如下方式:

?
1
2
3
4
5
6
7
8
9
public class Main{
      public static <T> void out(T t){
                System.out.println(t);
      }
      public static void main(String[] args){
              out("hansheng");
              out(123);
      }
}

所謂泛型方法,就是在聲明方法時定義一個或多個類型形參。泛型方法的用法格式如下:

?
1
2
3
4
修飾符<T, S> 返回值類型 方法名(形參列表)
方法體
 

注意:方法聲明中定義的形參只能在該方法里使用,而接口、類聲明中定義的類型形參則可以在整個接口、類中使用。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Demo{ 
     public <T> T fun(T t){   // 可以接收任意類型的數據 
         return t ;     // 直接把參數返回 
     
}
public class GenericsDemo26{ 
     public static void main(String args[]){ 
          Demo d = new Demo() ; // 實例化Demo對象 
          String str = d.fun("湯姆") ; // 傳遞字符串 
          int i = d.fun(30) ;  // 傳遞數字,自動裝箱 
          System.out.println(str) ; // 輸出內容 
          System.out.println(i) ;  // 輸出內容 
     
};

當調用fun()方法時,根據傳入的實際對象,編譯器就會判斷出類型形參T所代表的實際類型。

3.泛型構造器

正如泛型方法允許在方法簽名中聲明類型形參一樣,Java也允許在構造器簽名中聲明類型形參,這樣就產生了所謂的泛型構造器。

和使用普通泛型方法一樣沒區別,一種是顯式指定泛型參數,另一種是隱式推斷,如果是顯式指定則以顯式指定的類型參數為準,如果傳入的參數的類型和指定的類型實參不符,將會編譯報錯。

?
1
2
3
4
5
public class Person {
    public <T> Person(T t) {
        System.out.println(t);
    }
}
?
1
2
3
4
5
6
public static void main(String[] args){
        //隱式
        new Person(22);
        //顯示
        new<String>Person("hello");
    }

這里唯一需要特殊注明的就是,如果構造器是泛型構造器,同時該類也是一個泛型類的情況下應該如何使用泛型構造器:

因為泛型構造器可以顯式指定自己的類型參數(需要用到菱形,放在構造器之前),而泛型類自己的類型實參也需要指定(菱形放在構造器之后),這就同時出現了兩個菱形了,這就會有一些小問題,具體用法再這里總結一下。

以下面這個例子為代表

?
1
2
3
4
5
public class Person<E> {
    public <T> Person(T t) {
        System.out.println(t);
    }
}

這種用法:Person<String> a = new <Integer>Person<>(15); 這種語法不允許,會直接編譯報錯!

三、類型通配符

顧名思義就是匹配任意類型的類型實參。

類型通配符是一個問號(?),將一個問號作為類型實參傳給List集合,寫作:List<?>(意思是元素類型未知的List)。這個問號(?)被成為通配符,它的元素類型可以匹配任何類型

?
1
2
3
4
5
public void test(List<?> c){
    for(int i =0;i<c.size();i++){
        System.out.println(c.get(i));
    }
}

現在可以傳入任何類型的List來調用test()方法,程序依然可以訪問集合c中的元素,其類型是Object。

?
1
2
3
List<?> c = new ArrayList<String>();
        //編譯器報錯
        c.add(new Object());

但是并不能把元素加入到其中。因為程序無法確定c集合中元素的類型,所以不能向其添加對象。下面就該引入帶限通配符,來確定集合元素中的類型。

帶限通配符

簡單來講,使用通配符的目的是來限制泛型的類型參數的類型,使其滿足某種條件,固定為某些類。

主要分為兩類即:上限通配符和下限通配符。

1.上限通配符

如果想限制使用泛型類別時,只能用某個特定類型或者是其子類型才能實例化該類型時,可以在定義類型時,使用extends關鍵字指定這個類型必須是繼承某個類,或者實現某個接口,也可以是這個類或接口本身。。

?
1
2
它表示集合中的所有元素都是Shape類型或者其子類
List<? extends Shape>

這就是所謂的上限通配符,使用關鍵字extends來實現,實例化時,指定類型實參只能是extends后類型的子類或其本身。例如:

?
1
2
//Circle是其子類
List<? extends Shape> list = new ArrayList<Circle>();

這樣就確定集合中元素的類型,雖然不確定具體的類型,但最起碼知道其父類。然后進行其他操作。

2.下限通配符

如果想限制使用泛型類別時,只能用某個特定類型或者是其父類型才能實例化該類型時,可以在定義類型時,使用super關鍵字指定這個類型必須是是某個類的父類,或者是某個接口的父接口,也可以是這個類或接口本身。

?
1
2
它表示集合中的所有元素都是Circle類型或者其父類
List<? super Circle>

這就是所謂的下限通配符,使用關鍵字super來實現,實例化時,指定類型實參只能是extends后類型的子類或其本身。例如:

?
1
2
它表示集合中的所有元素都是Circle類型或者其父類
List<? super Circle>

四、類型擦除

?
1
2
3
Class c1 = new ArrayList<Integer>().getClass();
Class c2 = new ArrayList<String>().getClass();
System.out.println(c1==c2);

程序輸出:

true。

這是因為不管為泛型的類型形參傳入哪一種類型實參,對于Java來說,它們依然被當成同一類處理,在內存中也只占用一塊內存空間。從Java泛型這一概念提出的目的來看,其只是作用于代碼編譯階段,在編譯過程中,對于正確檢驗泛型結果后,會將泛型的相關信息擦出,也就是說,成功編譯過后的class文件中是不包含任何泛型信息的。泛型信息不會進入到運行時階段。

在靜態方法、靜態初始化塊或者靜態變量的聲明和初始化中不允許使用類型形參。由于系統中并不會真正生成泛型類,所以instanceof運算符后不能使用泛型類。

總結

本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關注服務器之家的更多內容!

原文鏈接:https://www.cnblogs.com/sgw1018/p/java-generic.html

延伸 · 閱讀

精彩推薦
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7472021-02-04
主站蜘蛛池模板: 色先锋av资源中文字幕 | 96萝莉 | 青草网在线观看 | 99热国产在线观看 | 草草影院国产 | 亚洲国产精品综合久久一线 | 国内精品久久久久影院中国 | 玩50岁四川熟女大白屁股直播 | 手机在线免费观看高清 | 亚洲精品资源在线 | 暴露狂婷婷 | 四虎在线播放 | 天天夜夜草草久久伊人天堂 | 国产乱码在线精品可播放 | 91久久精品国产亚洲 | 女人把扒开给男人爽的 | 欧美色精品天天在线观看视频 | 天天摸天天爽视频69视频 | 百合女女师生play黄肉黄 | 97涩色| 男人捅女人的鸡鸡 | 国产在线观看色 | 久久综合给合久久狠狠狠… | free性丰满hd性欧美人体 | 51国产| 欧美洲大黑香蕉在线视频 | 精品久久久久久影院免费 | 亚洲a在线视频 | 亚洲免费在线观看视频 | 韩国漂亮美女三级在线观看 | 国内揄拍国内精品久久 | 小早川怜子在线播放精品 | 国产女主播福利在线 | 欧美高清在线精品一区 | 精品亚洲永久免费精品 | 亚洲天堂99 | heyzo1754北岛玲在线视频 | 4438全国最大免费观看 | 免费看国产一级特黄aa大片 | 亚洲欧洲综合 | 国产v日韩v欧美v精品专区 |