一.介紹
泛型在java中有很重要的地位炸茧,在面向?qū)ο缶幊碳案鞣N設(shè)計模式中有非常廣泛的應用腻豌。
什么是泛型潮酒?為什么要使用泛型怠缸?
泛型桥爽,即“參數(shù)化類型”译蒂。一提到參數(shù)被饿,最熟悉的就是定義方法時有形參彩掐,然后調(diào)用此方法時傳遞實參医咨。那么參數(shù)化類型怎么理解呢枫匾?顧名思義,就是將類型由原來的具體的類型參數(shù)化拟淮,類似于方法中的變量參數(shù)干茉,此時類型也定義成參數(shù)形式(可以稱之為類型形參),然后在使用/調(diào)用時傳入具體的類型(類型實參)很泊。
泛型的本質(zhì)是為了參數(shù)化類型(在不創(chuàng)建新的類型的情況下角虫,通過泛型指定的不同類型來控制形參具體限制的類型)沾谓。也就是說在泛型使用過程中,操作的數(shù)據(jù)類型被指定為一個參數(shù)戳鹅,這種參數(shù)類型可以用在類均驶、接口和方法中,分別被稱為泛型類枫虏、泛型接口妇穴、泛型方法。
二.知識點介紹
1隶债、泛型的由來
2腾它、泛型使用
3、泛型聲明
三.上課對應視頻的說明文檔
1死讹、泛型的由來
先看一個案例:
List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);
for(int i = 0; i< arrayList.size();i++){
String item = (String)arrayList.get(i);
System.out.printf("泛型測試","item = " + item);
}
運行結(jié)果:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
ArrayList可以存放任意類型瞒滴,例子中添加了一個String類型,添加了一個Integer類型回俐,再使用時都以String的方式使用逛腿,因此程序崩潰了。為了解決類似這樣的問題(在編譯階段就可以解決)仅颇,泛型應運而生单默。
2、泛型的使用
泛型有三種使用方式忘瓦,分別為:泛型類搁廓、泛型接口、泛型方法
2.1耕皮、泛型類
泛型類型用于類的定義中境蜕,被稱為泛型類。通過泛型可以完成對一組類的操作對外開放相同的接口凌停。最典型的就是各種容器類粱年,如:List、Set罚拟、Map台诗。
泛型類的最基本寫法(這么看可能會有點暈,會在下面的例子中詳解)格式:
class 類名稱 <泛型標識:可以隨便寫任意標識號赐俗,標識指定的泛型的類型>{
private 泛型標識 /*(成員變量類型)*/ var;
.....
}
}
代碼示例:
//此處T可以隨便寫為任意標識拉队,常見的如T、E阻逮、K粱快、V等形式的參數(shù)常用于表示泛型
//在實例化泛型類時,必須指定T的具體類型
public class Generic<T>{
//key這個成員變量的類型為T,T的類型由外部指定?
private T key;
public Generic(T key) { //泛型構(gòu)造方法形參key的類型也為T,T的類型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值類型為T事哭,T的類型由外部指定
return key;
}
}
public class Test{
public static void main(String args[]){
//泛型的類型參數(shù)只能是類類型(包括自定義類)漫雷,不能是簡單類型
//傳入的實參類型需與泛型的類型參數(shù)類型相同,即為Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);
//傳入的實參類型需與泛型的類型參數(shù)類型相同慷蠕,即為String.
Generic<String> genericString = new Generic<String>("key_vlaue");
? System.out.println("泛型測試 : key is " + genericInteger.getKey());
System.out.println("泛型測試 : key is " + genericString.getKey());
}
}
//運行結(jié)果:
泛型測試: key is 123456
泛型測試: key is key_vlaue
2.2珊拼、泛型接口
泛型接口與泛型類的定義及使用基本相同。泛型接口常被用在各種類的生產(chǎn)器中:
代碼示例:
//定義一個泛型接口
public interface Generator<T> {
public T next();
}
/**
* 未傳入泛型實參時流炕,與泛型類的定義相同澎现,在聲明類的時候,需將泛型的聲明也一起加到類中
* 即:class FruitGenerator<T> implements Generator<T>{
* 如果不聲明泛型每辟,如:class FruitGenerator implements Generator<T>剑辫,編譯器會報錯:"Unknown class"
*/
class FruitGenerator<T> implements Generator<T>{
@Override
public T next() {
return null;
}
}
/**
* 傳入泛型實參時:
* 定義一個生產(chǎn)器實現(xiàn)這個接口,雖然我們只創(chuàng)建了一個泛型接口Generator<T>
* 但是我們可以為T傳入無數(shù)個實參,形成無數(shù)種類型的Generator接口渠欺。
* 在實現(xiàn)類實現(xiàn)泛型接口時妹蔽,如已將泛型類型傳入實參類型,則所有使用泛型的地方都要替換成傳入的實參類型
* 即:Generator<T>挠将,public T next();中的的T都要替換成傳入的String類型胳岂。
*/
public class FruitGenerator implements Generator<String> {
private String[] fruits = new String[]{"Apple", "Banana", "Pear"};
@Override
public String next() {
Random rand = new Random();
return fruits[rand.nextInt(3)];
}
}
2.3、泛型方法
在java中,泛型類的定義非常簡單舔稀,但是泛型方法就比較復雜了乳丰。
尤其是我們見到的大多數(shù)泛型類中的成員方法也都使用了泛型,有的甚至泛型類中也包含著泛型方法内贮,這樣在初學者中非常容易將泛型方法理解錯了产园。
泛型類,是在實例化類的時候指明泛型的具體類型夜郁;泛型方法什燕,是在調(diào)用方法的時候指明泛型的具體類型 。
代碼示例:
public class GenericTest {
//這個類是個泛型類竞端,在上面已經(jīng)介紹過
public class Generic<T>{? ?
private T key;
public Generic(T key) {
this.key = key;
}
//我想說的其實是這個屎即,雖然在方法中使用了泛型,但是這并不是一個泛型方法事富。
//這只是類中一個普通的成員方法剑勾,只不過他的返回值是在聲明泛型類已經(jīng)聲明過的泛型。
//所以在這個方法中才可以繼續(xù)使用 T 這個泛型赵颅。
public T getKey(){
return key;
}
}
/**
* 這才是一個真正的泛型方法。
* 首先在public與返回值之間的<T>必不可少暂刘,這表明這是一個泛型方法饺谬,并且聲明了一個泛型T
* 這個T可以出現(xiàn)在這個泛型方法的任意位置.
* 泛型的數(shù)量也可以為任意多個
*? ? 如:public <T,K> K showKeyName(Generic<T> container){
*? ? ? ? ...
*? ? ? ? }
*/
public <T> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
//當然這個例子舉的不太合適,只是為了說明泛型方法的特性。
T test = container.getKey();
return test;
}
public static void main(String[] args) {
}
}
代碼示例2:
public class Main {
public static <T> void out(T t) {
System.out.println(t);
}
public static void main(String[] args) {
out("findingsea");
out(123);
out(11.11);
out(true);
}
}
public class Main {
public static <T> void out(T... args) {
for (T t : args) {
System.out.println(t);
}
}
public static void main(String[] args) {
out("findingsea", 123, 11.11, true);
}
}
代碼示例3:
public class GenericFruit {
public static void main(String[] args) {
Apple apple = new Apple();
Person person = new Person();
GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
//apple是Fruit的子類募寨,所以這里可以
generateTest.show_1(apple);
//編譯器會報錯族展,因為泛型類型實參指定的是Fruit,而傳入的實參類是Person
//generateTest.show_1(person);
//使用這兩個方法都可以成功
generateTest.show_2(apple);
generateTest.show_2(person);
//使用這兩個方法也都可以成功
generateTest.show_3(apple);
generateTest.show_3(person);
}
}
class Fruit{
@Override
public String toString() {
return "fruit";
}
}
class Apple extends Fruit{
@Override
public String toString() {
return "apple";
}
}
class Person{
@Override
public String toString() {
return "Person";
}
}
class GenerateTest<T>{
public void show_1(T t){
System.out.println(t.toString());
}
//在泛型類中聲明了一個泛型方法拔鹰,使用泛型E仪缸,這種泛型E可以為任意類型×兄可以類型與T相同恰画,也可以不同。
//由于泛型方法在聲明的時候會聲明泛型<E>瓷马,因此即使在泛型類中并未聲明泛型拴还,編譯器也能夠正確識別泛型方法中識別的泛型。
public <E> void show_3(E t){
System.out.println(t.toString());
}
//在泛型類中聲明了一個泛型方法欧聘,使用泛型T片林,注意這個T是一種全新的類型,可以與泛型類中聲明的T不是同一種類型怀骤。
public <T> void show_2(T t){
System.out.println(t.toString());
}
}
3费封、泛型的聲明
可以用<T>、<K,V>蒋伦、<T? extends? Number>等進行泛型的聲明弓摘。其中,<T? extends? Number>的聲明方式限定了T的范圍凉敲,T只能為 Number的子類衣盾。
如:
E – Element (在集合中使用,因為集合中存放的是元素)
T – Type(Java 類)
K – Key(鍵)
V – Value(值)
N – Number(數(shù)值類型)
爷抓? – 表示不確定的java類型(無限制通配符類型)
Object – 是所有類的根類势决,任何類的對象都可以設(shè)置給該Object引用變量,使用的時候可能需要類型強制轉(zhuǎn)換蓝撇,但是用使用了泛型T果复、E等這些標識符后,在實際用之前類型就已經(jīng)確定了渤昌,不需要再進行類型強制轉(zhuǎn)換虽抄。