版權所有,轉載請注明出處
本文關鍵字:Generics 泛型 java泛型
Generic只是提供在編譯的時候提供一個類型信息,告訴編譯器與某個類(如List)相關的是String型還是Double型等信息,編譯時會檢查調用該類中的方法時所用的類型和返回的類型是否與該類相關的類型一致,編譯后的代碼并無特別之處.
特別注意:Java中引入泛型只是編譯時檢查,而且主要是檢查調用泛型類的類,運行時并不檢查,與1.4版的一樣
例如A類為泛型類,B類調用A類的方法,編譯時將會檢查B類的調用是否符合類型轉換要求.對A類來說泛型類T來說具體為什么類型,是不確定的,可以看作是當Object來處理的,而且對T賦予什么對象都會出現warn,但可正常運行.
如:對于泛型類(以下稱A):
package com.sily;
import java.util.ArrayList;
import java.util.List;
public class GenericClassA<T> {
public T p(T c) {
T t = (T) (new Double(23));
System.out.println(c + " " + c.getClass()+ " " + t);
return c;
}
public T d() {
T t = (T) (new Double(23));
return t;
}
public List<String> l() {
return new ArrayList<String>();
}
/**
* @param args
*/
}
import java.util.ArrayList;
import java.util.List;
public class GenericClassA<T>
public T p(T c) {
T t = (T) (new Double(23));
System.out.println(c + " " + c.getClass()+ " " + t);
return c;
}
public T d() {
T t = (T) (new Double(23));
return t;
}
public List<String>
return new ArrayList<String>
}
/**
* @param args
*/
}
以下為調用類(以下稱B):
package com.sily;
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
GenericClassA<Double> c = new GenericClassA<Double>();
String s = c.p(23125.9);
Double j = c.p(23125.34);
GenericClassA<String> e = new GenericClassA<String>();
String ddd = e.d();
GenericClassA<String> d = new GenericClassA<String>();
d.p(34.23);
}
}
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
GenericClassA<Double>
String s = c.p(23125.9);
Double j = c.p(23125.34);
GenericClassA<String> e = new GenericClassA<String>();
String ddd = e.d();
GenericClassA<String>
d.p(34.23);
}
}
會編譯出錯,是因為c.p(23125.9);這個方法返回的類型與定義的泛型不一致,但泛型類中的T t = (T) (new Double(23));只是警告.但可正常運行。
Double j = c.p(23125.34);這句可正常運行.
但是,String ddd = e.d();這句編譯沒問題,但運行時會出錯,因為e.d()方法實際返回的是一個Double型,如果轉換為String肯定出錯了,如果改成:
Double ddd = e.d();編譯時出錯,因為編譯器期望的返回類型是String,賦值給String型肯定編譯通不過
另外,還有一點,一般都只是A類定義時用到<T>,而B類只是調用,所以是具體的類型,如<String>,<Double>
所以說,泛型出現的目的只是為了限制調用類B調用A時參數類型不出錯的一種方式。
示例分析
對于調用類B中GenericClassA a=new GenericClassA ();也可看作是當Object處理,但與GenericClassA<Object> b = new GenericClassA<Object>();不同,如后者b.p(new Object())不會警告,但前者a.p(new Object())會警告,a的所有以泛型作參數的方法都會警告,這也是為了與1.4版本保持向下兼容
GenericClassA
即:
GenericClassA<Object> c = new GenericClassA<String>();
會出錯
但
GenericClassA a = new GenericClassA();
GenericClassA<String> c = new GenericClassA<String>();
a=c;
GenericClassA<String> c = new GenericClassA<String>();
a=c;
不會出錯,這也是為了與1.4版本保持向下兼容;
GenericClassA<?> a ;
GenericClassA<String> c = new GenericClassA<String>();
a=c;
也不會出錯
new GenericClassA<?>()不能實例化,因為類型不確定,調用它的相關方法如a.p("ddd")也會出錯,因為類型不確定,但可調用其它方法如a.toString();另外,GenericClassA<?>與GenericClassA<? extends Object>一樣
再看下面的代碼:
GenericClassA<String> c = new GenericClassA<String>();
Double dd = (Double)c.d();
會出錯,因為對于編譯器來說,c.d();返回的就是String類型,但在GenericClassA中,
該方法如下:
public T d() {
T t = (T) (new Double(23));
return t;
}
T t = (T) (new Double(23));
return t;
}
所以編譯時出錯,因為不能編譯,所以不能運行(應該是可運行的)
所以以下代碼是正確的
GenericClassA<Object> c = new GenericClassA<Object>();
Double dd = (Double)c.d();
Double dd = (Double)c.d();
實際上是對返回值作了強制類型轉換
另外,在java程序類的一個方法中,如p(Object c),則c可有"aaa"等來作參數,則會自動轉換為Object ,但在泛型中<Object>是不能自動轉換的,即GenericClassA<Object> c = new GenericClassA<String>();會出錯
GenericClassA<?> c = new GenericClassA<String>();
c.p("abc");會出錯
因為對編譯器來說c是一個未知類型(可能是Object及其子類)的GenericClassA,有可能是Double的GenericClassA,所以如果p("abc")會出錯
補充:
要成功運行以上示例代碼,請下載最新版jdk:http://java.sun.com/j2se/1.5.0/download.jsp
另外,Eclipse3.1已經對java5提供的支持,大家可在Eclipse3.1中調試以上代碼
小結:
以上為對java5的新特征——泛型的一點小小的見解,更詳細的內容請參考sun的tutorial:
泛型只是java5的新特征之一,其它特征如變參,元數據,靜態導入,增強循環等會在以后陸續介紹,敬請關注
補充(2005-12-14):
1.如果在spring的getHibernateTemplate().find(sql)返回的類型為List<?>(因為類型不確定),那么在多的UnitDAO的getUnitByName()方法中應該返回List<UnitDTO>,這個問題怎么解決?
參考資料:
作者簡介:
施祖陽,網名sylilzy,1979年生。
2002年起從事軟件開發工作,主要研究JAVA、Linux及相關技術。
你可通過sylilzy@163.com與作者聯系。