學習猶如感情,只有為之付出,才會有所得。
一、Java泛型入門基礎
1、 泛型歷史:集合中可以儲存任意型別物件,但是在取出時,如果要使用具體物件的特有方法時,需要進行向下轉型,如果儲存的物件型別不一致,在轉型過程中就會出現ClassCastException異常。這樣就給程式帶來了不安全性。
在jdk1.5以後就有了解決方案——泛型技術:在儲存元素時,就不允許儲存不同型別的元素。儲存了就編譯失敗。 所以就需要在儲存元素時,在容器上明確具體的元素型別,這其實和陣列定義很像。
2、優勢:1)將執行時期的ClassCastException異常轉移到了編譯時期,進行檢查,並以編譯失敗來體現。 這樣有利於程式設計師儘早解決問題。
2)避免了向下轉型(強轉)的麻煩。
3、在什麼情況下使用泛型呢?
只要在使用類或者介面時,該類或者介面在api文當描述時都帶著<>,就需要在使用時定義泛型。
其實,泛型無非就是通過<>定義了一個形式引數,專門用於接收具體的引用型別。在使用時,一定要傳遞對應的實際引數型別。
集合中泛型的應用特別多見。
二、Java泛型使用
使用泛型的動機舉例(以集合為例):
class MySet:(自行寫的MySet,實現的Java泛型)
public class MySet<E> { private Object[] objs=new Object[0]; public boolean add(E obj){ if(contains(obj)){ return false; } Object tempObjs[] = new Object[objs.length+1]; System.arraycopy(objs, 0, tempObjs, 0, objs.length); tempObjs[objs.length] = obj; objs = tempObjs; return true; } public boolean contains(E obj){ for(Object o:objs){ if(o.equals(obj)){ return true; } } return false; } public Object[] getAll(){ return objs; } public int size(){ return objs.length; } }
測試class:
import java.util.ArrayList; import java.util.Iterator; public class GenericDemo2 { public static void main(String[] args) { // MySet<String> set = new MySet<String>(); // set.add("abcd"); // set.add(8);//編譯出錯 // MySet<Integer> set = new MySet<Integer>(); // set.add("abcd");//編譯出錯 // set.add(8); } }
注意:當一個變數被宣告為泛型時,只能被例項變數和方法呼叫,而不能被靜態變數和方法呼叫。原因很簡單,引數化的泛型是一些例項。靜態成員是被類的例項和引數化的類所共享的,所以靜態成員不應該有型別引數和他們關聯。
三、Java泛型類
1、概念:當一個類要操作的引用資料型別不確定的時候,可以將該型別定義一個形參。用到的這類時,由使用者來通過傳遞型別引數的形式,來確定要操作的具體的物件型別。這意味著在定義這個類時,需要在類上定義形參,用於接收具體的型別實參。這就是將泛型定義在類上,即泛型類。
2、什麼時候使用泛型類呢?
只要類中操作的引用資料型別不確定的時候,就可以定義泛型類。 有了泛型類,省去了曾經的強轉和型別轉換異常的麻煩。
程式碼示例:
/*
* 泛型類的演示
*/
public class GenericDemo2 {
public static void main(String[] args) {
MyVessel<Worker> u = new MyVessel<Worker>();
//u.setObject(new Student());//不行,u中存放的是Worker,實參只能是Worker型別
u.setObject(new Worker());
Worker w = u.getObject();
System.out.println(w);
MyVessel<Student> v = new MyVessel<Student>();
//v.setObject(new Worker());//不行,實參只能是Student型別
}
}
class MyVessel<E>{ //從語法上講把“E”取成別的名字如“QQ”也是可以的,但不規範。
private E obj;
public void setObject(E obj){
this.obj = obj;
}
public E getObject(){
return obj;
}
}
class Student{
String profession;
}
class Worker{
String company;
}
四、Java泛型方法
1、泛型方法的定義(與類的泛型捆綁)
方法要操作的型別不確定的,但是和呼叫該方法的物件指定的型別是一致。
2、泛型方法的定義(獨立於類的泛型)
方法要操作的型別不確定的,而且不一定和呼叫該方法的物件指定的型別是一致。
3、泛型方法的定義(靜態方法的泛型)
靜態方法不能訪問類上定義的泛型,因為它沒有物件。如果靜態方法需要泛型,該泛型只能定義在方法上。
程式碼示例:
class Demo<W>{
//方法上的泛型和類的一致,或者說依賴於類的泛型
public void show(W w){
System.out.println("show:"+w.toString());
}
//不帶泛型,不安全。因為在呼叫方可以把該方法的返回值強轉成其它型別,從而出現強轉異常
public Object myprint0(Object a){
System.out.println("myprint:"+a);
return a;
}
//方法帶泛型,但要求和類的泛型相互獨立。可以限定返回型別和方法的實參相同,更安全,而且不用強轉。
public <A> A myprint(A a){
System.out.println("myprint:"+a);
return a;
}
//靜態方法帶泛型,泛型一定要獨立於類,因為它沒有物件。
public static <A> A myprint2(A a){
System.out.println("myprint:"+a);
return a;
}
}
五、Java泛型介面
這個不好解釋直接程式碼示例了:
interface Inter<V>{
public abstract V show(V v);
}
//實現泛型介面的類的定義。 方法中的引數型別和類宣告實現介面時指定的型別一致,且已經確定為String!-----本例假設我們寫這個類的時候知道該類就是專門處理String型資料的
class InterImpl implements Inter<String>{
@Override
public String show(String s) {
System.out.println(s);
return s;
}
}
//實現泛型介面的類的定義。 方法中的引數型別和類宣告實現介面時指定的型別一致,但不確定!-----本例假設我們寫這個類的時候不知道該類是處理什麼型別的資料的,但有一點確定:宣告類物件時指定什麼型別(泛型的實參),show方法就處理該型別
class InterImpl2<C> implements Inter<C>{
@Override
public C show(C s) {
System.out.println(s);
return s;
}
}
六、Java泛型高階
(PS:關於Java泛型的高階應用,最低要求:雖然自己不一定能寫出這樣類似的定義,但是你必須會使用或呼叫(其他程式設計師或API中已經採用這種技術寫的類或方法),所以這裡只做最簡單的介紹,有興趣的博友可以去深入學習,博主能力有限,嘿嘿。。)
1、泛型的萬用字元:?
當操作的不同容器中的型別都不確定的時候,而且使用的都是元素從Object類中繼承的方法, 這時泛型就用萬用字元?來表示即可。(助理解的比方: 泛型中的多型應用)
2、泛型的限定:
對操作的型別限制在一個範圍之內。比如:定義一個功能,只操作Person型別或者Person的子型別。這時可以用:
? extends E:接收E型別或者E的子型別。這就是上限。
? super E: 接收E型別或者E的父型別。 這就是下限。
一般情況下:
只要是往容器中新增元素時,使用上限。 ? extends E
只要是從容器中取出元素時,是用下限。 ? super E
高階只有這麼多,博友可以去API中看看這種情況,例如:Collection