java泛型(一)--泛型的簡單介紹以及常用情況

一.泛型的基本概念

泛型是JavaSE5引入的一個新概念,實現了參數化類型的概念,並且使代碼可以應用於多種類型。

在日常編寫程序時,我們都會很注重“泛化”,像多態其實就是一種類型的泛化,將子類的對象賦給父類的引用獲得更好的泛化特性,將方法的參數類型設置爲父類以獲得泛化特性等等......  同樣,爲類聲明出接口,而後對接口進行操作同樣也可以獲得很好的泛化特性。但是上述的方法都有一定的侷限性,那就是繼承與實現。繼承引起了類與類之間的強耦合關係,實現接口又可能會實現一些不需要的接口。我們想去實現代碼能應用於一個“不具體的類型”,而不是具體的類與接口。泛型的出現很好的幫我們實現了這一點!


二.泛型的簡單使用

1.泛型與容器類的聯合使用

考慮一種情況,我們需要實現一個自己的容器類,這個類僅僅持有一個對象的引用(假設持有的對象是字符串),在JavaSE5之前,我們可以編寫出如下代碼:

package com.fsc.generic;

public class MyHolder {
	private String s;
	public MyHolder(String s){
		this.s = s;
	}
	public String getS() {
		return s;
	}
	public void setS(String s) {
		this.s = s;
	}
}
代碼很簡單,也實現了持有一個對象。但是如果除了需要持有字符串外還要持有其他類呢?難道要再寫一個MyHolder2類麼?在泛型出現以前我們可以編寫如下代碼:

package com.fsc.generic;

public class MyHolder {
	private Object obj;
	public MyHolder(Object obj){
		this.obj = obj;
	}
	public Object getObj() {
		return obj;
	}
	public void setObj(String obj) {
		this.obj = obj;
	}
}
來看一下使用的情況:

MyHolder holderForString = new MyHolder("A String");
String s = (String) holderForString.getObj();
需要對返回的數據進行強制轉換,並且很容易出現問題,若我們將返回值轉化爲Integer,那麼在運行期就會出現ClassCastException。


與上述代碼類似,在JavaSE5以前的集合類就是用Object的數組來儲存數據,每次我們向裏面添加什麼對象都不予以限制,在返回時需要程序員手動通過強制轉換來還原對象。問題在於編譯器無法得到對象的實際類型,如果我傳入了一個String對象,而在返回時將其返回成Integer的話,運行期的虛擬機就會很不客氣的拋出一個ClassCastException,就像這樣:

ArrayList list = new ArrayList();
list.add("123");
list.add(1);
Integer i = (Integer) list.get(0);

而泛型的作用就是將異常轉移到編譯器,下面是添加了泛型後的程序:

ArrayList<String> list = new ArrayList<String>();
list.add("123");
//list.add(1);   不允許向集合添加非String類型的對象
		
String s = list.get(0);
編譯器阻止了我們添加不正確的對象,也不需要我們手動進行轉型,極大的避免了上述情形的發生。


2.泛型實現元組

很多時候我們希望retrue可以返回多個對象,在這種情況下一般就是創建一個類,讓它持有需要返回的對象。考慮這樣一種情況,我們需要返回非常多組(假設每組中有兩個對象)數據,我們就需要爲這些對象創建很多的類。有了泛型,我們就能很好的解決這個問題。

public class TwoTuple<A,B> {
	public final A first;
	public final B second;
	
	public TwoTuple(A first, B second){
		this.first = first;
		this.second = second;
	}
	
	@Override
	public String toString() {
		return "first: "+ first+"  second: "+second;
	}
}

需要返回數據時我們只需要返回一個元組即可。


三.泛型的定義規則

1.泛型類的定義

在前面實現了MyHolder類,現在我們將其定義爲泛型類:

package com.fsc.generic;

public class MyHolder<T> {
	private T t;
	public MyHolder(T t){
		this.t = t;
	}
	public T getT() {
		return t;
	}
	public void setT(T t) {
		this.t = t;
	}
}

僅僅需要在類名後面加上<T>即可,而後可以在類中使用T,不過只能將其作爲一種類型參數來使用,而不能當做是一個真正的類。具體原因會在下面的文章中介紹。


2.泛型接口的定義

<span style="font-size:18px;color:#000000;">public interface GenericInterface<T>{
	public T get();
}</span>
可以看到和類的定義方式基本相同。

3.泛型方法的定義

public class GenericMethod {
	public static <T> void f(T x){
	}
	public <T> void g(T x){
		
	}
}
泛型方法的定義比較特殊,它不需要將泛型作用在整個類上。是否擁有泛型方法,與其所在的類是否是泛型沒有關係。在Thingking in java一書中給出了指導原則:如果泛型方法可以取代對整個類進行泛化時,就應該只使用泛型方法。對於靜態的(static)方法,其無法訪問泛型類的類型參數(T),所以如果要在static方法中使用類型參數,那麼就必須要使用泛型方法。


針對泛型方法的類型參數推斷

在使用泛型類時,如果我們需要創建一個對象,就需要制定類型參數的值,像這樣:

ArrayList<String> list = new ArrayList<String>();

new ArrayList<String>()中的類型參數String是沒有意義的(在JavaSE7中已經可以使用<>不寫參數來創建對象了),在使用泛型方法的時候,我們可以不用寫明參數類型,編譯器會自動爲我們找出類型,示例中使用的方法爲上面實現的GenericMethod.class:

f("123");
f(1);
f(1.0);
編譯器自動爲我們找出了類型,我們可以和調用正常的方法一樣調用f().






下一篇文章中將詳細說明泛型的擦除,邊界,通配符等一些問題。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章