泛型的使用


一,泛型概述

​ 關於泛型,先來說幾句集合。都知道集合是可以存儲任意對象,當我們創建一個集合時如果沒有聲明它的存儲類型,那該集合便自動提升為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

八,總結

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

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

感謝閱讀!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM