程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA編程入門知識 >> java 泛型(Generic)

java 泛型(Generic)

編輯:JAVA編程入門知識

  java泛型(generic)

2016-07-11

問題

  1. 向集合存儲元素存在安全性問題,即很可能不知道已有的集合中已經存儲的元素的類型是什麼,這時就不應該向該集合隨便添加元素,否則與集合的設計的初衷相悖,因為集合存儲的是同類型的元素,而且以後取集合中的元素不知道該轉為什麼類型。
  2. 從集合中獲取元素需要強制類型轉換,可能出現ClassCastException問題

    未使用泛型舉例:

 

    說明:任何Object子類均可以存入Object集合,但是從集合中獲取元素,則是Object對象,需要強制轉換,可能拋異常。

意義

使用泛型能夠限定集合中存放的元素類型,標記集合中的元素類型,對存入集合的元素進行類型檢查,讓集合中的元素類型一致。泛型就是“參數化類型”,類比於形參和實參的關系,泛型就是讓原本具體類型進行參數化,在泛型具體化或者泛型調用的時候,將類型實參傳給類型形參,例如String這個實參傳給T形參。

使用

未使用泛型

 public void test1(){
         //未使用泛型
         List list=new ArrayList();
         list.add(1);
         //加入類型不一致
         list.add("one");
         for(int i=0;i<list.size();i++){
             //可能出現ClassCastException
             int num=(int) list.get(i);
         }
     }

使用泛型

 

 public void test2(){
         //使用泛型,保證了集合使用的安全性
         List<Integer> list=new ArrayList<>();
         list.add(1);
         //不能加入其它類型
 //        list.add("one");
         for(int i=0;i<list.size();i++){
             //不會出現ClassCastException
             int num=list.get(i);
         }
     }

使用細則

自定泛型類、泛型接口、泛型方法

泛型類及方法的使用

 class Animal<T>{
     
 }
 
 //Cat繼承Animal
 //指定T為Short,Cat為具體類,不是泛型類
 class Cat extends Animal<Short>{
 }
 
 //泛型類Persion繼承Animal
 //還未指定T,Persion是泛型類
 class Persion<T> extends Animal<T>{
     private T t;
     List<T> list=new ArrayList<>();
     public T getT(){
         return this.t;
     }
     public void setT(T t){
         this.t=t;
     }
     //泛型方法
     public <E> E change(E e){
         return e;
     }
 }
 
 public class Generic_Use{
     public static void main(String[] args) {
         //泛型類使用
         {
             //在實例化泛型類時,未指定泛型的具體類型,則泛型的默認類型為Object
             Persion persion=new Persion();
             persion.setT("helloWorld");
             //persion.getT() 返回Object類型  需要強制轉換為String
             String str=(String) persion.getT();
         }
         {
             //在實例化泛型類時,指定了泛型的具體類型A,則所有出現泛型的地方均為類型A
             Persion<String> persion=new Persion<>();
             persion.setT("helloWorld");
             //persion.getT() 返回String類型
             System.out.println(persion.getT());
             {
                 //泛型方法的具體化
                 //persion.change(11)均返回Integer類型
                 System.out.println(persion.change(11));
                 //通用的泛型方法具體化
                 System.out.println(persion.<Integer>change(11));
                 //出錯 無法指定類泛型具體類型為Integer的方法又用String進行 具體化
 //            System.out.println(persion.<Integer>change("12"));
             }
         }
     }

 

 泛型接口的使用類似泛型類,可類比使用

泛型與繼承

 public class Generic_extends {
     public static void main(String[] args) {
         {
             //普通類與繼承
             Object object=null;
             String str="str";
             object=str;
             
             Object[] objects=null;
             String[] strs=new String[]{"str1","str2"};
             objects=strs;
         }
         
         {
             //泛型類與繼承
             List<Object> list=null;
             List<String> list2=null;
             //出錯    List<String> 不是List<Object>的父類
 //            list=list2;
             {
                 //證明
                 //反證:若List<Object>是 List<String>的父類
                 list.add(11);
                 String str=list2.get(0);//會出錯
                 
                 //結論:若A是B的父類,但是List<A>不是List<B>的父類
             }
         }
     }
 }

泛型的通配符

原則:對於集合的使用,則是嚴進寬出,即放入集合要類型准確一致,取出可以模糊取出

注意點:靜態方法和catch塊不能使用泛型

建議:參考C++中的模板編程思想

 public void test3(){
         //通配符?
         {
             //List<?>是所有List<對象> 的父類
             //List<?>可以指向任意的List<對象>實例對象,並取出其中的元素,但是不能放入入元素
             
             List<?> list=new ArrayList<>();
             List<Object> list2=new ArrayList<>();
             List<String> list3=new ArrayList<>();
             list2.add(12);
             list3.add("bbb");
             list=list2;
             list=list3;
             
             {
                 //list取出存在list3的元素
                 Iterator<?> iterator=list.iterator();
                 while(iterator.hasNext()){
                     System.out.println(iterator.next());
                 }
                 
                 //list不能直接通過自己向他的子類放入元素
                 //list向list3中存入元素
 //            list.add("ccc");//出錯
                 
                 //特例 空對象
                 list.add(null);
                 
                 
             }
         }
         
         {
             //邊界通配符  ? extends A 
             //允許集合中的元素類型是A及其子類(類型通配符上限 類比小於號《)
             
             List<? extends Number> list4=new ArrayList<>();
             List<Integer> list5=new ArrayList<>();
             list5.add(12);
             list4=list5;
             //list4取出存在list5中的元素
             System.out.println(list4.get(0));
             //list4向list5中存入元素
 //            list4.add(12);//出錯
         }
         
         {
             //邊界通配符 ? super A
             //允許集合中的元素類型是A及其父類(類型通配符下限 類比大於號》)
             
             List<? super Integer> list6=new ArrayList<>();
             List<Integer> list7=new ArrayList<>();
             list7.add(12);
             
             list6=list7;
             //list6取出存在list7中的元素
             System.out.println(list6.get(0));
             //list6向list7中存入元素
 //            list6.add(12);//出錯
             
 //        list7=list6;//出錯
         }
         
         //結論:
         //對於泛型,父類可以從子類中取元素,但是不能存元素到子類中
         //或者說可以從通配符泛型類中取出元素,但是不能寫入元素
         //體現了,集合中的元素嚴進寬出,寫入集合的元素需要具體類型,而讀取集合的元素沒有要求
     }
 }

泛型提高

 public void test4(){
         List<Integer> list=new ArrayList<>();
         List<String> list2=new ArrayList<>();
         
         System.out.println("list的類型="+list.getClass());
         System.out.println("list2的類型="+list2.getClass());
         //結果:
         //list的類型=class java.util.ArrayList
         //list2的類型=class java.util.ArrayList
         
     }

從上面程序可以看出方法test4中list和list2的實際運行類型是一致的。

這關系到泛型的類型擦除。

原因分析:泛型設計的初衷就是在程序編寫的時候就能夠進行類型檢查,避免程序運行時出錯。對於泛型類型檢查通過後,所以在編譯時,會將泛型的相關信息擦出,也就是說,成功編譯過後的class文件中是不包含任何泛型信息的。其實就是好比C++中模板編程,所有同一個泛型的具體化對象實際均是同一類型,所有同一泛型的具體化對象其實是共享同一份代碼,即母代碼,可以類比java中類和實例之間的關系學習。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved