泛型的使用

  • 2019 年 10 月 3 日
  • 筆記

一,泛型概述

​ 關於泛型,先來說幾句集合。都知道集合是可以存儲任意對象,當我們創建一個集合時如果沒有聲明它的存儲類型,那該集合便自動提升為Object類型。請參看如下程式碼:

public class GenericDemo {      public static void main(String[] args) {          Collection coll = new ArrayList();          coll.add("abc");          coll.add("hello");          coll.add(5);//由於集合沒有做任何限定,任何類型都可以給其中存放          // 使用迭代器遍歷集合          Iterator it = coll.iterator();          while(it.hasNext()){      //需要列印每個字元串的長度,就要把迭代出來的對象轉成String類型              String str = (String) it.next();              System.out.println(str.length());          }      }  }

​ 毫無疑問,以上程式碼會報錯,首先從程式碼上看在集合中存儲了數值類型,字元串類型。但是在使用迭代器遍歷時,取出的是String類型的,那麼對於數值類型來說就必然會報錯。

​ 那麼以上問題該怎麼解決呢?通常對於Collection集合來說,同一個集合對象只保存一種數據類型。如下程式碼所示:

        // 存儲字元類型          Collection<String> stringCollection = new ArrayList<>();          // 存儲數值類型          Collection<Integer> integerCollection = new ArrayList<>();          // 存儲精度類型          Collection<Double> doubleCollection = new ArrayList<>();

​ 也就是說在我們初始化集合的時候,就已經將存儲存儲類型固定。但是按照Java的靈活性來說,這是顯然不夠的,比如預先並不確定要存儲的類型。因此在JDK5之後新增了泛型的語法,使我們的程式顯得更為靈活。

​ 泛型:在方法或者類(介面)中預先的使用某種未知的數據類型。

​ 提示:在我們創建對象的時候,如果沒有明確指出一種數據類型,那麼編譯器會默認為Object類。

1.1,泛型的意義

​ 那說了這麼多,泛型又能解決什麼問題呢?針對上面的案例分析:

​ 1,可以將ClassCastException異常由運行期轉到編譯期異常。

​ 2,避免強制類型的轉換。

​ 請看如上程式碼,在我們初始化集合時指定String數據類型,此時再存儲數值類型就會在編譯期報錯,那麼泛型又將如何運用呢。先別急,我們先看看集合中是如何定義的。

​ 在Array List集合源碼中看出,它的存儲類型是一個E,這是什麼呢?

  • E,Elements:元素
  • T,Type:類型

​ 這就是泛型,因為我們對於集合來說它們並不知道我們要存儲是什麼類型,因此這就是泛型的意義。

​ 提示:泛型是數據類型的一部分,我們將類名與泛型合併一起看做數據類型。

​ 泛型,用來靈活地將數據類型應用到不同的類、方法、介面當中。將數據類型作為參數進行傳遞。

二,泛型類

​ 編寫格式:

修飾符 class 類名稱<泛型變數>{}

​ 我們還是以集合為例,請看如下集合源程式碼實現,以add方法作為參考。

​

​ 可以看出add方法接收的數據類型為E,與我們上一節圖片中所示Array List源碼實現是一致的,因為Array List接收的參數類型就是E。說這麼多有些啰嗦,現在先定義一個泛型類。

public class GenericClass<T> {      private T key;     public void setKey(T key){         this.key = key;     }     public T getKey(){         return key;     }  }
public class GenericDemo {      public static void main(String[] args) {           GenericClass<String> genericString = new GenericClass<>();         // 存儲字元類型          genericString.setKey("這是一個泛型類");         // 獲取數據          String stringKey = genericString.getKey();          System.out.println(stringKey);            // 存儲數值類型          GenericClass<Integer> genericInteger = new GenericClass<>();          // 存儲數值1          genericInteger.setKey(1);          // 獲取數值          Integer integerKey = genericInteger.getKey();          System.out.println(integerKey);      }  }

​

三,泛型方法

​ 編寫格式:

修飾符 <泛型變數> 返回值類型 方法名稱(參數){}

​ 例如定義泛型方法:

public class GenericMethod {      // 定義泛型方法      public <T> void menthod(T t) {          System.out.println(t.getClass());      }      // 返回實例化對象      public <T> T getGenericMethod(Class<T> t) throws IllegalAccessException, InstantiationException {          return t.newInstance();      }      public static void main(String[] args) throws Exception {          GenericMethod genericMethod = new GenericMethod();          genericMethod.menthod("abc");          Object method = genericMethod.getGenericMethod(Class.forName("com.api.generic.User"));          System.out.println(method);      }  }  

四,泛型介面

​ 編寫格式:

修飾符 interface 介面名稱<泛型變數>{}

​ 定義泛型介面:

public interface GenericInterface<T> {        void add(T t);        T getT();  }

​ 如果泛型介面的實現類仍不確定,則還可以接著使用泛型。

public class GenericInterfaceImpl<T> implements GenericInterface<T> {      @Override      public void add(T t) {      }        @Override      public T getT() {          return null;      }  }

​ 只有在創建對象的時候,要確定泛型的數據類型。

GenericInterfaceImpl<String> genericInterface = new GenericInterfaceImpl<>();          genericInterface.add("Hello");

五,泛型通配符

​ 當使用泛型類或者介面時,傳遞的數據中,泛型類型不確定,可以通過通配符<?>表示。但是一旦使用泛型的通配符後,只能使用Object類中的共性方法,集合中元素自身方法無法使用。

通配符基本使用

泛型的通配符:不知道使用什麼類型來接收的時候,此時可以使用?,?表示未知通配符。

此時只能接收數據,不能往該集合中存儲數據。

舉個例子大家理解使用即可:

public static void main(String[] args) {      Collection<Intger> list1 = new ArrayList<Integer>();      getElement(list1);      Collection<String> list2 = new ArrayList<String>();      getElement(list2);  }  public static void getElement(Collection<?> coll){}  //?代表可以接收任意類型
注意:泛型不存在繼承關係 Collection<Object> list = new ArrayList<String>();這種是錯誤的。

六,泛型上下限

​ 之前設置泛型的時候,實際上是可以任意設置的,只要是類就可以設置。但是在JAVA的泛型中可以指定一個泛型的上限和下限。

泛型的上限:

  • 格式: 類型名稱 <? extends 類 > 對象名稱
  • 意義: 只能接收該類型及其子類

泛型的下限:

  • 格式: 類型名稱 <? super 類 > 對象名稱
  • 意義: 只能接收該類型及其父類型

比如:現已知Object類,String 類,Number類,Integer類,其中Number是Integer的父類

public static void main(String[] args) {      Collection<Integer> list1 = new ArrayList<Integer>();      Collection<String> list2 = new ArrayList<String>();      Collection<Number> list3 = new ArrayList<Number>();      Collection<Object> list4 = new ArrayList<Object>();        getElement(list1);      getElement(list2);//報錯      getElement(list3);      getElement(list4);//報錯        getElement2(list1);//報錯      getElement2(list2);//報錯      getElement2(list3);      getElement2(list4);    }  // 泛型的上限:此時的泛型?,必須是Number類型或者Number類型的子類  public static void getElement1(Collection<? extends Number> coll){}  // 泛型的下限:此時的泛型?,必須是Number類型或者Number類型的父類  public static void getElement2(Collection<? super Number> coll){}

七,泛型擦除

​ 在泛型的使用中還有一項重要的概念,就是泛型擦除。

​ 這裡就不再分享了,有一篇寫的很好的部落格關於泛型擦除的,有興趣的可以去看看。

​ 轉載自:https://blog.csdn.net/briblue/article/details/76736356

八,總結

​ 總體來說泛型的出現為我們的程式碼帶來很多便利,使程式更加靈活。並且在官方文檔中提出如果能使用泛型就盡量使用泛型,提高程式碼的可讀性。

​ 這次關於泛型的總結相對來說並不深入,只是一些簡單的描述。最後,在本文中的內容如有不適之處,歡迎多多指點。

感謝閱讀!

Tags: