第三部分 泛型接口以及泛型方法
1、接口泛型
接口使用泛型與類使用泛型沒有太大的區別
interface InterGenerics<T>{ T next();}
//實現接口InterGenerics,生成Fibonacci數列
class Fibonacci implements InterGenerics<Integer>{
private int count = 0;
//將int型自動裝箱成它所對應的包裝類Integer
public Integer next(){return fib(count++);}
private int fib(int n){
if(n<2) return 1;
return fib(n-2) + fib(n-1);
}
}
通過上例我們可以看到java泛型的一個侷限性:基本類型是無法作爲類型參數的。不過java SE5及之後版本已經解決了這個問題,具有自動裝箱和拆箱(也稱打包和拆包)的功能。將基本類型int,double等裝箱成Integer,Double等包裝類,是自動裝箱過程,反之則是自動拆箱過程
2、泛型方法
在定義泛型方法時,只需將泛型參數列表置於返回類型之前
public class GenericMethod {
public <T> void f(T t){
System.out.println(t.getClass());
}
public static void main(String[] args){
GenericsMethod gm = new GenericMethod();
gm.f("test");
gm.f(2.8);
gm.f(2.8f);
gm.f('q');
}
}
泛型方法能夠讓該方法獨立於類而發生改變。在我們的代碼設計當中應當儘量使用泛型方法。
注意:對於一個static方法,它是無法訪問泛型類的類型參數,如果static需要使用泛型,就必須將它自身方法變爲泛型方法
這裏還有一點:我們在使用泛型類創建其對象時,必須指定類型參數的值,就像下面:
class Example<T>{
public static void main(){
Example<String> e = new Example<String>();
}
}
而我們在使用泛型方法時不必指明參數,編譯器會自動找出具體類型,這個過程叫做:類型參數推斷(來自Java In Thinking)
可變參數與泛型方法
import java.util.*;
public class GenericVarargs {
public static <T> List<T> getList(T...args) {
List<T> container = new LinkedList<T>();
for(T item: args)
container.add(item);
return container;
}
public static void main(String[] args){
List<String> ls1 = getList("exmple");
System.out.println(ls1);
List<Character> ls2 = getList('a');
System.out.println(ls2);
}
}
第四部分 邊界
淺談擦除問題:
import java.util.*;
public class ErasedTypeEquivalence {
public static void main(String[] args){
List<String> c1 = new ArrayList<String>();
List<Integer> c2 = new ArrayList<Integer>();
System.out.println(c1.getClass()==c2.getClass());
}
}/*output:
true
*/
上面代碼的輸出結果告訴我們在這個程序中`ArrayList<String>與ArrayList<Integer>`是相同的類型,這是爲什麼呢?
這是由於java泛型是由擦除來實現的,也就是當我們使用泛型時,任何具體的類信息都會被擦除。因此`List<String>和List<Integer>`在運行時實際是相同的類。這兩種類被擦除成它們的“原生”類型List.
再來看這樣一個例子
class HasF{
public void f(){System.out.println("HasF.f()");}
}
class Operator<T> {
private T obj;
public Operator(T obj){this.obj = obj;}
//operate方法中的obj試圖調用HasF類當中的f()
//出現編譯器報錯:The method f() is undefined for the type T
public void operate(){obj.f();}
}
public class Operation{
public static void main(String[] String){
HasF hf = new HasF();
Operator<HasF> operator = new Operator<HasF>(hf);
operator.operate();
}
}
造成以上錯誤原因是:由於有了擦除,雖然Operator在創建對象時將HasF對象最爲參數傳入,但是在Operator由於參數類型T是不確定的,所以它內部的operate方法卻無法通過java編譯器將自己必須在obj上調用f()這一需求映射到HasF擁有f()這一事實上。
於是我們引進邊界來對參數類型T做一個限定。限制T必須是HasF的派生類,這裏將重用extends關鍵字,修改後的代碼如下:
class HasF{
public void f(){System.out.println("HasF.f()");}
}
//制定T的邊界HasF
class Operator<T extends HasF> {
private T obj;
public Operator(T obj){this.obj = obj;}
//此時調用f()編譯器不會報錯,代碼正常運行
public void operate(){obj.f();}
}
public class Operation{
public static void main(String[] String){
HasF hf = new HasF();
Operator<HasF> operator = new Operator<HasF>(hf);
operator.operate();
}
}
以上就是邊界的概念。其實所謂邊界,簡單說就是爲泛型的參數類型做一個限定