黑馬程式設計師——Java集合框架(二)之泛型
培訓、java培訓、java學習型技術部落格、期待與您交流!------------
泛型
一、泛型概述
1.什麼是泛型?
泛型就是指將資料型別引數化,把以前固定的資料型別用一個代表資料型別的引數進行表示,該引數可以接受傳入的任意資料型別。可以這樣理解,如果把常量數值1當做一種資料型別,那麼將常量數值1變成變數a的過程就可以理解為引數化過程,此時a既可以接受常量數值1的資料型別,也可以接受常量數值2、3等的資料型別,那麼此時變數a就可以理解為泛型。
泛型是jdk1.5版本出現的新特性,用於解決安全問題,是一個安全機制。
2.泛型的好處
a)將執行時期出現的問題ClassCastException,轉移到了編譯時期。方便於程式設計師解決問題。讓執行時期問題減少、安全。
b)避免了強制轉換的麻煩。如在實現某一個介面時,指定傳入介面方法的實參的型別的話,在複寫該介面方法時就可以直接使用指定型別,而不需要強制轉換。
3.泛型格式
通過<>來定義要操作的引用資料型別。
泛型:
格式:<T>
示例:ArrayList<T>,表示該集合存入的元素為T型別,T可以為任意物件型別。
泛型例項:泛型例項就是將泛型引數例項化。
格式:<具體資料型別>
示例:ArrayList<String>,表示該集合存入的元素必須為String型別。
4.萬用字元
萬用字元也稱佔位符,表示格式為:<?>
示例:ArrayList<?>,表示該集合存入的元素為任意物件型別,?表示佔位符,也稱萬用字元。
5.泛型注意事項
1)泛型是提供給javac編譯器使用,生成的位元組碼檔案會去掉“型別”資訊,使程式執行效率不受影響,對引數化的泛型型別,getClass()方法的返回值和原始型別完全一樣。
2)由於編譯生成的位元組碼會去掉泛型的型別資訊,只要能跳過編譯器,就可以往某個泛型集合中加入其它型別的資料,如用反射得到集合,再呼叫add方法即可。
6.問題思考
1)在使用java提供的物件時,什麼時候寫泛型?
答:泛型通常在集合框架中很常見,只要見到<>就要定義泛型。其實<>就是用來接收型別的。當使用集合時,將集合中要儲存的資料型別作為引數傳遞到<>中即可。
二、泛型類
1.什麼是泛型類
當泛型定義在類名上時,該類就叫泛型類。
2.泛型類的格式
泛型類:
class 類名<T> { 類的內容}
其中,T為泛型引數,類中內容可以存在使用T的方法或成員變數或返回值型別或引數型別等。
泛型例項類:是指在類中定義泛型,並且泛型引數已經例項化,即已經確定資料型別。
calss 類名<具體資料型別> { 類的內容 }
例如:class 類名<String> { 類的內容 },表示將類中String型別提升為Object型別,在類中String物件不能使用其特有方法,且靜態方法中不能存在String物件。
示例1:定義泛型引數類
class Utils<QQ>
{
private QQ q;
public void setObject(QQ q)
{
this.q=q;
}
public QQ getObject()
{
return q;
}
}
示例2:定義泛型例項類
class Person<Character>
{
private int age;
private String name;
public void setName(String name)
{
this.name=name;
}
public void setAge(int age)
{
this.age=age;
}
}
3.程式碼練習
/*
練習1:自定一個泛型類,完成對任意物件的操作。
*/
class GenericTest1
{
public static void main(String[] args)
{
Utils<Worker> u=new Utils<Worker>();
u.setObject(new Worker());
Worker w=u.getObject();
}
}
class Worker
{
}
//定義一個泛型類
class Utils<QQ>
{
private QQ q;
public void setObject(QQ q)
{
this.q=q;
}
public QQ getObject()
{
return q;
}
}<strong>
</strong>
三、泛型方法
1.概述
在方法上定義泛型,該方法就稱為泛型方法。
2.格式:
修飾符 <T> 返回值型別 函式名稱(引數型別1 名稱1,...)
例如:public <T> void show(T t){ 方法內容 }
3.示例:
在類中定義泛型方法:
class PrintDemo
{
//定義泛型方法
public <T> void print(T t)
{
System.out.println(t);
}
}
四、靜態泛型方法
1.概述
在靜態方法上定義泛型,該靜態方法就稱為靜態泛型方法。
2.格式:
修飾符 static <T> 返回值型別 函式名稱(引數型別1 名稱1,...)
例如:public static <T> void show(T t){ 方法內容 }
3.示例:
在類中定義靜態泛型方法:
class PrintDemo
{
//定義靜態泛型方法
public static <T> void print(T t)
{
System.out.println(t);
}
}
五、泛型介面
1.概述
在介面上定義泛型,該介面就稱為泛型介面。
2.格式:
泛型介面:
interface 介面名<T> { 介面的內容 }
泛型例項介面:
interface 介面名<具體資料型別> { 介面的內容 }
示例:interface 介面名<String> { 介面的內容 },表示將該介面中的String型別提升為Object型別,String物件只能呼叫Object來中的方法,而不能呼叫其特有方法。
3.程式碼練習:
<span style="font-size:14px;">/*
練習2:定義泛型介面,並利用泛型介面進行操作。
*/
class GenericTest2
{
public static void main(String[] args)
{
/*
InterImp i=new InterImp();
i.show("hello");
*/
InterImp<Integer> i=new InterImp<Integer>();
i.show(2);
}
}
//定義一個泛型介面
interface Inter<T>
{
void show(T t);
}
/*
class InterImp implements Inter<String>
{
public void show(String s)
{
System.out.println("show run:"+s);
}
}
*/
//定義一個類,實現泛型介面,併成為泛型引數類
class InterImp<T> implements Inter<T>
{
public void show(T t)
{
System.out.println("show run:"+t);
}
}</span>
程式執行後的結果如下圖:
六、泛型限定
1.概述
泛型限定是指將泛型的引數型別限定範圍。
2.格式
1)限定上限:<? extends E>
示例:ArrayList<? extends E>,表示該集合存入的元素只能為E型別或E的子型別。
2)限定下限:<? super E>
示例:ArrayList<? super E>,表示該集合存入的元素只能為E型別或E的父型別。
3.程式碼練習:
練習3:利用泛型上限操作不同物件資料型別的集合。
import java.util.*;
/*
練習3:利用泛型上限操作不同物件資料型別的集合。
*/
class GenericTest3
{
public static void main(String[] args)
{
ArrayList<Person> al=new ArrayList<Person>();
al.add(new Person("abc01"));
al.add(new Person("abc02"));
al.add(new Person("abc03"));
ArrayList<Student> all=new ArrayList<Student>();
all.add(new Student("java01"));
all.add(new Student("java02"));
all.add(new Student("java03"));
printArr(al);
printArr(all);
}
public static void printArr(ArrayList<? extends Person> a)
{
Iterator<? extends Person> i=a.iterator();
while (i.hasNext())
{
System.out.println(i.next().getName());
}
}
}
class Person
{
private String name;
Person(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
}
class Student extends Person
{
Student(String name)
{
super(name);
}
}
程式執行後的結果如下圖:
練習4:利用泛型下限操作不同物件型別的集合。
import java.util.*;
class GenericTest4
{
public static void main(String[] args)
{
TreeSet<Student> ts=new TreeSet<Student>(new Comp());
ts.add(new Student("student05"));
ts.add(new Student("student03"));
ts.add(new Student("student02"));
TreeSet<Worker> tss=new TreeSet<Worker>(new Comp());
tss.add(new Worker("worker04"));
tss.add(new Worker("worker03"));
tss.add(new Worker("worker06"));
for (Iterator i=ts.iterator();i.hasNext() ; )
{
Student s=(Student)i.next();
System.out.println(s.getName());
}
for (Iterator i=tss.iterator();i.hasNext() ; )
{
Worker w=(Worker)i.next();
System.out.println(w.getName());
}
}
}
//泛型引數指定Person,表示可以接受Person的子型別,即可以接收Worker和Student
class Comp implements Comparator<Person>
{
public int compare(Person p1,Person p2)
{
return p1.getName().compareTo(p2.getName());
}
}
class Person
{
private String name;
Person(String name)
{
this.name=name;
}
public String getName()
{
return name;
}
}
class Student extends Person
{
Student(String name)
{
super(name);
}
}
class Worker extends Person
{
Worker(String name)
{
super(name);
}
}
程式的執行結果如下圖:
七、總結
1.類上定義泛型只有兩種情況,泛型類和泛型例項類,即為 class 類名<T> {}和class 類名<具體資料型別> {},且格式唯一。方法上定義泛型只有一種情況,就是在返回值型別前加泛型<T>。
2.對於泛型引數類,類中的內容都可以引用該泛型引數,但不包括靜態方法,因為靜態方法先於物件存在。對於泛型例項類,如果某個資料型別被例項化,例如class 類名<String> { 類的內容 },則表示將類中String型別提升為Object型別,在類中String物件不能使用其特有方法,且靜態方法中不能存在String物件。對於泛型方法中的泛型引數只能在方法上或方法內中引用。如果類中出現了泛型引數,但在方法和類上都沒有定義泛型,則編譯不通過。
3.泛型限定中的泛型引數必須在方法中或類中有定義,否則不通過。泛型例項和萬用字元只能出現在定義了泛型的類或介面名上。
4.泛型型別的物件和萬用字元物件不能呼叫其特有方法,只能呼叫Object類中的方法。
5.萬用字元?只能作為泛型的佔位符使用,表示不確定接受型別。不能作為引數資料型別進行引用。
6.泛型的問題:為了方便說明問題,假定存在三個類,分別為Person、Teacher、Student,且存在Student繼承Teacher,而Teacher繼承Person的關係。
1)<T super 具體資料型別>,如<T super Student>,是不允許出現這種格式的。
示例1:public <T superStudent> void print(Collection<T > coll){}:錯誤格式。
示例2:public <T> voidprint(Collection<T super Student> coll){}:錯誤格式。
2)<T extends 具體資料型別>,如<T extendsPerson>,只能出現在泛型方法定義上。
示例1:public <T> voidprint(Collection<T extends Person> coll){}:<Textends Person>沒有出現在泛型方法定義中,而是在其他地方,所以格式錯誤。
示例2:public <T extends Person>void print(Collection<T > coll){}:<T extends Person>出現在泛型方法定義上,所以格式正確。
3)<? super T>和<? extends T>不能用來定義泛型類、泛型介面和泛型方法。
示例1:public<? extends T> void print(Collection<T > coll){}:<? extends T>不能用來定義泛型方法,格式錯誤。
示例2:public <T> voidprint(Collection<? extends T> coll){}:格式正確。
示例3 : public <? super T> void print(Collection<T> coll){}: <? super T>不能用來定義泛型方法,格式錯誤。
示例4:public <T> void print(Collection<?super T> coll){}:格式正確。
4) <? super T>、<? extends T>和<T>必須依賴於泛型類或泛型介面或泛型方法而存在,應為泛型引數T必須要進行定義。
在泛型方法中,下面三種寫法在泛型限定上是等效的,即接收的型別都為Person或Person的子類。
格式1:public <T extends Person> voidprint(Collection<? super T> coll){}
格式2:public <T extends Person> voidprint(Collection<T> coll){}
格式3:public <T extends Person> voidprint(Collection<? extends T> coll){}
在泛型類或介面中,下面三種寫法在泛型限定上是不同的。
例如:存在一個泛型類A<T>,包含三種格式,如下:
classA<T>
{
格式1:public void print(Collection<? super T> coll){} :當newA<Student>時,print方法只能接收泛型為Student或Student的父類的集合。
格式2:public void print(Collection<T> coll){}:當newA<Student>時,print方法只能接收泛型為Student的集合。
格式3:public void print(Collection<? extends T> coll){}:當new A<Student>時,print方法只能接收泛型為Student或Student的子類的集合。
}
5)<?super 具體資料型別>、<? extends 具體資料型別>和<具體資料型別>不需要依賴於泛型類或泛型介面或泛型方法而存在,因為三種都不存在泛型引數,因此不需要進行定義。
6)型別不相容問題。
情況1:ArrayList<String> al=new ArrayList<Object>();
情況2:ArrayList<Object> al=new ArrayList<String>();
7)編譯出現安全提醒。
情況1:ArrayList<String> al=new ArrayList();
注:ArrayList al=new ArrayList<String>();不出現安全提醒,因為泛型是jdk1.5版本出現的,老版本應該容納新版本。