1. 程式人生 > >學習猶如感情,只有為之付出,才會有所得。

學習猶如感情,只有為之付出,才會有所得。

 一、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