泛型的作用與定義
類型的參數化,就是可以把類型像方法的參數那樣傳遞
泛型使編譯器可以在編譯期間對類型進行檢查以提高類型安全,減少運行時由於對象類型不匹配引發的異常。
1. 泛型是什么
一說到泛型,大伙肯定不會陌生,我們代碼里面有很多類似這樣的語句:
List list=new ArrayList<>();
ArrayList就是個泛型類,我們通過設定不同的類型,可以往集合里面存儲不同類型的數據類型(而且只能存儲設定的數據類型,這是泛型的優勢之一)。“泛型”簡單的意思就是泛指的類型(參數化類型)。想象下這樣的場景:如果我們現在要寫一個容器類(支持數據增刪查詢的),我們寫了支持String類型的,后面還需要寫支持Integer類型的。然后呢?Doubel、Float、各種自定義類型?這樣重復代碼太多了,而且這些容器的算法都是一致的。我們可以通過泛指一種類型T,來代替我們之前需要的所有類型,把我們需要的類型作為參數傳遞到容器里面,這樣我們算法只需要寫一套就可以適應所有的類型。最典型的的例子就是ArrayList了,這個集合我們無論傳遞什么數據類型,它都能很好的工作。
聰明的同學看完上面的描述,靈機一動,寫出了下面的代碼:
class MyList{ private Object[] elements=new Object[10]; private int size; public void add(Object item) {
elements[size++]=item;
} public Object get(int index) { return elements[index];
}
}
這個代碼靈活性很高,所有的類型都可以向上轉型為Object類,這樣我們就可以往里面存儲各種類型的數據了。的確Java在泛型出現之前,也是這么做的。但是這樣的有一個問題:如果集合里面數據很多,某一個數據轉型出現錯誤,在編譯期是無法發現的。但是在運行期會發生java.lang.ClassCastException。例如:
MyList myList=new MyList();
myList.add("A");
myList.add(1);
System.out.println(myList.get(0));
System.out.println((String)myList.get(1));
我們在這個集合里面存儲了多個類型(某些情況下容器可能會存儲多種類型的數據),如果數據量較多,轉型的時候難免會出現異常,而這些都是無法在編譯期得知的。而泛型一方面讓我們只能往集合中添加一種類型的數據,同時可以讓我們在編譯期就發現這些錯誤,避免運行時異常的發生,提升代碼的健壯性。
2. Java泛型介紹
下面我們來介紹Java泛型的相關內容,下面會介紹以下幾個方面:
-
Java泛型類
-
Java泛型方法
-
Java泛型接口
Java泛型類
類結構是面向對象中最基本的元素,如果我們的類需要有很好的擴展性,那么我們可以將其設置成泛型的。假設我們需要一個數據的包裝類,通過傳入不同類型的數據,可以存儲相應類型的數據。我們看看這個簡單的泛型類的設計:
class DataHolder<T>{
T item; public void setData(T t) { this.item=t;
} public T getData() { return this.item;
}
}
泛型類定義時只需要在類名后面加上類型參數即可,當然你也可以添加多個參數,類似於,等。這樣我們就可以在類里面使用定義的類型參數。
泛型類最常用的使用場景就是“元組”的使用。我們知道方法return返回值只能返回單個對象。如果我們定義一個泛型類,定義2個甚至3個類型參數,這樣我們return對象的時候,構建這樣一個“元組”數據,通過泛型傳入多個對象,這樣我們就可以一次性方法多個數據了。
Java泛型方法
前面我們介紹的泛型是作用於整個類的,現在我們來介紹泛型方法。泛型方法既可以存在於泛型類中,也可以存在於普通的類中。如果使用泛型方法可以解決問題,那么應該盡量使用泛型方法。下面我們通過例子來看一下泛型方法的使用:
class DataHolder<T>{
T item; public void setData(T t) { this.item=t;
} public T getData() { return this.item;
} /**
* 泛型方法
* @param e
*/
public void PrinterInfo(E e) {
System.out.println(e);
}
}
我們來看運行結果:
1AAAAA8.88
從上面的例子中,我們看到我們是在一個泛型類里面定義了一個泛型方法printInfo。通過傳入不同的數據類型,我們都可以打印出來。在這個方法里面,我們定義了類型參數E。這個E和泛型類里面的T兩者之間是沒有關系的。哪怕我們將泛型方法設置成這樣:
//注意這個T是一種全新的類型,可以與泛型類中聲明的T不是同一種類型。public void PrinterInfo(T e) {
System.out.println(e);
}//調用方法DataHolder dataHolder=new DataHolder<>();
dataHolder.PrinterInfo(1);
dataHolder.PrinterInfo("AAAAA");
dataHolder.PrinterInfo(8.88f);
這個泛型方法依然可以傳入Double、Float等類型的數據。泛型方法里面的類型參數T和泛型類里面的類型參數是不一樣的類型,從上面的調用方式,我們也可以看出,泛型方法printInfo不受我們DataHolder中泛型類型參數是String的影響。我們來總結下泛型方法的幾個基本特征:
-
public與返回值中間非常重要,可以理解為聲明此方法為泛型方法。
-
只有聲明了的方法才是泛型方法,泛型類中的使用了泛型的成員方法並不是泛型方法。
-
表明該方法將使用泛型類型T,此時才可以在方法中使用泛型類型T。
-
與泛型類的定義一樣,此處T可以隨便寫為任意標識,常見的如T、E、K、V等形式的參數常用於表示泛型。
Java泛型接口
Java泛型接口的定義和Java泛型類基本相同,下面是一個例子:
//定義一個泛型接口public interface Generator<T> { public T next();
}
此處有兩點需要注意:
- 泛型接口未傳入泛型實參時,與泛型類的定義相同,在聲明類的時候,需將泛型的聲明也一起加到類中。例子如下:
/* 即:class DataHolder implements Generator{
* 如果不聲明泛型,如:class DataHolder implements Generator,編譯器會報錯:"Unknown class"
*/class FruitGenerator<T> implements Generator<T>{ @Override
public T next() { return null;
}
}
- 如果泛型接口傳入類型參數時,實現該泛型接口的實現類,則所有使用泛型的地方都要替換成傳入的實參類型。例子如下:
class DataHolder implements Generator<String>{ @Override
public String next() { return null;
}
}
從這個例子我們看到,實現類里面的所有T的地方都需要實現為String。
轉載出處:https://blog.csdn.net/weixin_39888180/article/details/111104741