1.爲什麼需要泛型?
public class GenericTest {
public static void main(String[] args) {
List arrayList = new ArrayList();
arrayList.add("zhang");
arrayList.add("genericTest");
arrayList.add(2017);
for (int i=0; i<arrayList.size(); i++) {
//1.java.lang.ClassCastException
//String message = (String) arrayList.get(i);
//System.out.println("message:"+message);
//2.OK
Object message = arrayList.get(i);
System.out.println(message.getClass()+" "+"message:"+message);
}
}
}
定義一個ArrayList類型的集合,先向其中加入兩個字符串類型的值,隨後加入一個Integer類型的集合,此處完全沒問題,因爲此時的arrayList默認的類型爲Object類型。在之後的for循環中由於忘記了arrayList類中含有Integer類型的元素,將Integer類型強制轉換爲String類型,出現1處的錯誤。
在上面的編碼過程中,主要存在兩個問題:
(1)將一個對象放入到一個集合中,集合不會記住此對象的類型,當再次從集合中取出此對象時,由於該對象在編譯類型爲Object類型,但其運行時類型依然爲其本身類型,所以在1處運行時出現 Java.lang.ClassCastException 異常,導致此類錯誤在編譯過程中不易被發現。
(2)取出集合元素時,需要人爲強制轉化爲具體的目標類型
2.什麼是泛型?
泛型,即“參數化類型”,顧名思義,就是將原來的具體的類型參數化,就像方法中的變量參數,此時類型也定義成參數形式(“類型形參”),然後在使用時傳入具體的類型(”類型實參”)。
將上面的例子採用泛型的寫法。
public class GenericTest {
public static void main(String[] args) {
List<String> arrayList = new ArrayList<String>();
arrayList.add("zhang");
arrayList.add("genericTest");
//arrayList.add(2017);1.提示編譯錯誤
for (int i=0; i<arrayList.size(); i++) {
String message = arrayList.get(i);//2
System.out.println("message:"+message);
}
}
}
採用泛型寫法後,在1處加入一個Integer類型的對象時會出現編譯錯誤,通過List<String>限定arrayList集合中只能含有String類型的元素,因此在2處不需要強制類型轉換。3.泛型接口、泛型類和泛型方法
(1)泛型類定義格式:
訪問權限 class 類名稱<泛型類型標識1,泛型類型標識2,..泛型類型標識n>{
訪問權限 泛型類型標識 變量名稱;
訪問權限 泛型類型標識 方法名稱(){};
訪問權限 返回值類型聲明 方法名稱(泛型類型標識 變量1,泛型類型標識 變量2,...){}
(2)泛型對象的定義格式:
類名稱<具體類> 對象名稱 = new 類名稱<具體類>();
package generic;
class Point<T> {
private T var;
public void setVar(T var) {
this.var = var;
}
public T getVar() {
return this.var;
}
}
public class GenericClass {
public static void main(String[] args) {
Point<String> name = new Point<String>();
name.setVar("zhang");
System.out.println(name.getVar());
}
}
在泛型接口、泛型類和泛型方法的定義過程中,常見 T、E、K、V 形式的參數表示泛型形參,思考:對於不同傳入的類型實參,生成的相應對象實例類型是不是一樣呢?
package generic;
public class GenericType {
public static void main(String[] args) {
Point<String> stringPoint = new Point<String>();
Point<Integer> integerPoint = new Point<Integer>();
System.out.println(stringPoint.getClass());//class generic.Point
System.out.println(integerPoint.getClass());//class generic.Point
System.out.println(stringPoint.getClass() == integerPoint.getClass());//true
}
}
由此發現,在使用泛型時,雖然傳入了不同的泛型實參,但並沒有真正意義上生成不同的類型,泛型類在內存中只有一個,即還是原來最基本的類型(本例中爲Point)。泛型只作用於代碼編譯階段,成功編譯過後的class文件中是不包含任何泛型信息的,泛型信息不會進入到運行時階段,泛型類型在邏輯上可以看作是多個不同的類型,實際上都是相同的基本類型。
4.類型通配符
接着上面內容,思考:Point<Number>和Point<Integer>實際上都是Point類型,那麼在邏輯上,是否可以將二者看成具有父子關係的泛型類型呢?簡單例子:
package generic;
public class GenericGeneral {
public static void main(String[] args) {
Point<Number> name = new Point<Number>();
Point<Integer> age = new Point<Integer>();
getVar(name);
//The method getVar(Point<Number>) in the type GenericGeneral is not applicable
//for the arguments (Point<Integer>)
//getVar(age);1
}
public static void getVar(Point<Number> data) {
System.out.println("data:"+data.getVar());
}
}
編譯上面代碼,在1處提示錯誤信息,因此在邏輯上Point<Number>不能視爲Point<Integer>的父類。因此引入類型通配符的概念,邏輯上用來表示這種繼承關係。看下面類型通配符使用實例:
package generic;
public class Generic_01 {
public static void main(String[] args) {
Point<String> name = new Point<String>();
Point<Integer> age = new Point<Integer>();
Point<Number> number = new Point<Number>();
getVar(name);
getVar(age);
getVar(number);
}
public static void getVar(Point<?> data) {
System.out.println(data.getVar());
}
}
類型通配符上限和類型通配符下限,比如在上面的例子上,定義一個同樣的相似的getVar方法,但是實參類型進一步限制,只能是Number類型及其子類,具體實例如下:
package generic;
public class Generic_01 {
public static void main(String[] args) {
Point<String> name = new Point<String>();
Point<Integer> age = new Point<Integer>();
Point<Number> number = new Point<Number>();
getVar(name);
getVar(age);
getVar(number);
//getUpperNumberVar(name);1
getUpperNumberVar(number);2
getUpperNumberVar(age);3
}
public static void getVar(Point<?> data) {
System.out.println(data.getVar());
}
public static void getUpperNumberVar(Point<? extends Number> data) {
System.out.println(data.getVar());
}
}
在1處代碼將會出現錯誤提示,2和3處調用正常。
類型通配符下限形式爲Point<? super Numer>,其含義與類型通配符上限正好相反。