jdk6和jdk5相比的新特性有:
一、instrumentation
在 Java SE 6 裏面,instrumentation 包被賦予了更強大的功能:啓動後的 instrument、本地代碼 instrument,以及動態改變 classpath 等等。
二、Http有所加強
三、 Java 管理擴展(JMX) 架構及其框架,以及在 Java SE 5 中新引入的 JMX API -- java.lang.management 包
四、JDK 6 中新增長編譯器 API(JSR 199)。利用此 API,您能夠在運行時調用 Java 編譯器,能夠編譯不一樣形式的源代碼文件,還能夠採集編譯器的診斷信息。
五、Java DB 和 JDBC 4.0
六、對腳本語言的支持
Java SE 6 新引入的對 JSR 223 的支持,它旨在定義一個統一的規範,使得 Java 應用程序能夠經過一套固定的接口與各類腳本引擎交互,從而達到在 Java 平臺上調用各類腳本語言的目的。
七、XML API 與 Web 服務
Java SE 6 中提供的 XML 處理框架,以及在此框架之上結合註釋(Annotation) 技術,所提供的強大的針對 Web 服務的支持
要詳細的能夠參看:
http://www.ibm.com/developerworks/cn/java/j-lo-jse6/java
多了很多新特性
一.Instrumentation
利用 Java 代碼,即 java.lang.instrument 作動態 Instrumentation 是 Java SE 5 的新特性,它把 Java 的 instrument 功能從本地代碼中解放出來,使之能夠用 Java 代碼的方式解決問題。在 Java SE 6 裏面,instrumentation 包被賦予了更強大的功能:啓動後的 instrument、本地代碼(native code)instrument,以及動態改變 classpath 等等。在 Java SE 5 當中,開發者只能在 premain 當中施展想象力,所做的 Instrumentation 也僅限與 main 函數執行前,這樣的方式存在必定的侷限性。在 Java SE 6 的 Instrumentation 當中,有一個跟 premain「並駕齊驅」的「agentmain」方法,能夠在 main 函數開始運行以後再運行。
2、Http
在 Java SE 6 當中,圍繞着 HTTP 協議出現了不少實用的新特性:NTLM 認證提供了一種 Window 平臺下較爲安全的認證機制;JDK 當中提供了一個輕量級的 HTTP 服務器;提供了較爲完善的 HTTP Cookie 管理功能;更爲實用的 NetworkInterface;DNS 域名的國際化支持等等。
HTTP Cookie管理能夠應用客戶操做臨時變量的保存,如查詢條件,當前狀態等
3、JMX與系統管理
管理系統的構架
圖 1. 管理系統構架
上圖分析了管理系統的基本構架模式。其中 Agent / SubAgent 起到的就是翻譯的做用:把 IT 資源報告的消息以管理系統能理解的方式傳送出去。
也許讀者有會問,爲何須要 Agent 和 SubAgent 兩層體系呢?這裏有兩個現實的緣由:
管理系統通常是一箇中央控制的控制軟件,而 SubAgent 直接監控一些資源,每每和這些資源分佈在同一物理位置。當這些 SubAgent 把狀態信息傳輸到管理系統或者傳達管理系統的控制指令的時候,須要提供一些網絡傳輸的功能。
管理系統的消息是有必定規範的,消息的翻譯自己是件複雜而枯燥的事情。
通常來講,管理系統會將同一物理分佈或者功能相似的 SubAgent 分組成一組,由一個共用的 Agent 加以管理。在這個 Agent 裏封裝了 1 和 2 的功能。
JMX 和管理系統
JMX 既是 Java 管理系統的一個標準,一個規範,也是一個接口,一個框架。圖 2 展現了 JMX 的基本架構。
圖 2. JMX 構架
和其它的資源系統同樣,JMX 是管理系統和資源之間的一個接口,它定義了管理系統和資源之間交互的標準。javax.management.MBeanServer 實現了 Agent 的功能,以標準的方式給出了管理系統訪問 JMX 框架的接口。而 javax.management.MBeans 實現了 SubAgent 的功能,以標準的方式給出了 JMX 框架訪問資源的接口。而從類庫的層次上看,JMX 包括了核心類庫 java.lang.management 和 javax.management 包。java.lang.management 包提供了基本的 VM 監控功能,而 javax.management 包則向用戶提供了擴展功能。
JMX幫助開發者監控JVM的信息
4、編輯器API
JDK 6 提供了在運行時調用編譯器的 API。在傳統的 JSP 技術中,服務器處理 JSP 一般須要進行下面 6 個步驟:
分析 JSP 代碼;
生成 Java 代碼;
將 Java 代碼寫入存儲器;
啓動另一個進程並運行編譯器編譯 Java 代碼;
將類文件寫入存儲器;
服務器讀入類文件並運行;
但若是採用運行時編譯,能夠同時簡化步驟 4 和 5,節約新進程的開銷和寫入存儲器的輸出開銷,提升系統效率。實際上,在 JDK 5 中,Sun 也提供了調用編譯器的編程接口。然而不一樣的是,老版本的編程接口並非標準 API 的一部分,而是做爲 Sun 的專有實現提供的,而新版則帶來了標準化的優勢。
新 API 的第二個新特性是能夠編譯抽象文件,理論上是任何形式的對象 —— 只要該對象實現了特定的接口。有了這個特性,上述例子中的步驟 3 也能夠省略。整個 JSP 的編譯運行在一個進程中完成,同時消除額外的輸入輸出操做。
第三個新特性是能夠收集編譯時的診斷信息。做爲對前兩個新特性的補充,它可使開發人員輕鬆的輸出必要的編譯錯誤或者是警告信息,從而省去了不少重定向的麻煩
5、Java DB 與jdbc4.0
新安裝了 JDK 6 的程序員們也許會發現,除了傳統的 bin、jre 等目錄,JDK 6 新增了一個名爲 db 的目錄。這即是 Java 6 的新成員:Java DB。這是一個純 Java 實現、開源的數據庫管理系統(DBMS),源於 Apache 軟件基金會(ASF)名下的項目 Derby。
從 Java 6 開始,應用程序再也不須要顯式地加載驅動程序了,DriverManager 開始可以自動地承擔這項任務。
6、對腳本語言的支持
Java SE 6 引入了對 Java Specification Request(JSR)223 的支持,JSR 223 旨在定義一個統一的規範,使得 Java 應用程序能夠經過一套固定的接口與各類腳本引擎交互,從而達到在 Java 平臺上調用各類腳本語言的目的。
Java 腳本 API 爲開發者提供了以下功能:
獲取腳本程序輸入,經過腳本引擎運行腳本並返回運行結果,這是最核心的接口。
發現腳本引擎,查詢腳本引擎信息。
經過腳本引擎的運行上下文在腳本和 Java 平臺間交換數據。
經過 Java 應用程序調用腳本函數。
7、XML API 與 Web 服務程序員
1.4與1.5區別數據庫
1 泛型
2 自動裝箱/拆箱
3 for-each
4 static import
5 變長參數編程
1. 泛型 1.4以前 java util包中容器類,裝的是Object對象,你要裝特定的類型能夠,但要強制轉換,這可能致使運行時錯誤.
例:原來ArrayList list=new ArrayList();
list.add(new Integer(3));
list.add(new Integer(4));
int i=((Integer)(list.get(0))).parseInt();
很麻煩
如今ArrayListlist=new ArrayList();
list.add(new Integer(3));
list.add(new Integer(4));
int i=list.get(0).parseInt();
不用Cast,運行時錯誤變爲編譯時錯誤,這是進步.
相似與C++中的摸板templete.但機理不一樣.
2 自動裝箱/拆箱
仍是剛纔例子
最後一句可改成
int i=list.get(0);
原始類型與對應的包裝類不用顯式轉換,方便
3 for-each
循環的加強
int a[]={........};//初始化,略
for(int i:a)
{
......
}
不用之前的i=0;i<a.length;i++ <br="">4 static import
之前調Java.math
Math.sqrt();
如今 static import java.lang.Math.sqrt;
再 sqrt();
至關於你本身類裏有這個方法
5 變長參數
int sum(int ...intlist)
{
int i,sum;
sum=0;
for(int i=0;i<intlist.length;i++) <br="">{
sum+=list[i];
}
return sum; }
有任意個參數,把他看做數組沒多大區別,只要你注意別用那些過期的方法就行,如果新版本加的或有改變的方法,docs裏會說的,在方法解釋下面數組
jdk6.0將會有很大變化
加強的for循環
爲了迭代集合和數組,加強的for循環提供了一個簡單、兼容的語法.有兩點值得一提:安全
Init表達式
在循環中,初始化表達式只計算一次.這意味着您一般能夠移除一個變量聲明.在這個例子中,咱們必須建立一個整型數組來保存computeNumbers()的結果,以防止每一次循環都從新計算該方法.您能夠看到,下面的代碼要比上面的代碼整潔一些,而且沒有泄露變量numbers:
未加強的For:服務器
int sum = 0;網絡
Integer[] numbers = computeNumbers();架構
for (int i=0; i < numbers.length ; i++)框架
sum += numbers[i];
加強後的For:
int sum = 0;
for ( int number: computeNumbers() )
sum += number;
侷限性
有時須要在迭代期間訪問迭代器或下標,看起來加強的for循環應該容許該操做,但事實上不是這樣,請看下面的例子:
for (int i=0; i < numbers.length ; i++) {
if (i != 0) System.out.print(",");
System.out.print(numbers[i]);
}
咱們但願將數組中的值打印爲一個用逗號分隔的清單.咱們須要知道目前是不是第一項,以便肯定是否應該打印逗號.使用加強的for循環是沒法獲知這種信息的.咱們須要本身保留一個下標或一個布爾值來指示是否通過了第一項. 這是另外一個例子:
for (Iterator it = n.iterator() ; it.hasNext() ; )
if (it.next() < 0)
it.remove();
在此例中,咱們想從整數集合中刪除負數項.爲此,須要對迭代器調用一個方法,可是當使用加強的for 循環時,迭代器對咱們來講是看不到的.所以,咱們只能使用Java 5以前版本的迭代方法. 順便說一下,這裏須要注意的是,因爲Iterator是泛型,因此其聲明是Iterator.許多人都忘記了這一點而使用了Iterator的原始格式.
註釋
註釋處理是一個很大的話題.由於本文只關注核心的語言特性,因此咱們不打算涵蓋它全部的可能形式和陷阱. 咱們將討論內置的註釋(SuppressWarnings,Deprecated和Override)以及通常註釋處理的侷限性.
Suppress Warnings
該註釋關閉了類或方法級別的編譯器警告.有時候您比編譯器更清楚地知道,代碼必須使用一個被否決的方法或執行一些沒法靜態肯定是否類型安全的動做,而使用:
@SuppressWarnings("deprecation")
public static void selfDestruct() {
Thread.currentThread().stop();
}
這多是內置註釋最有用的地方.遺憾的是,1.5.0_04的javac不支持它.可是1.6支持它,而且Sun正在努力將其向後移植到1.5中.
Eclipse 3.1中支持該註釋,其餘IDE也可能支持它.這容許您把代碼完全地從警告中解脫出來.若是在編譯時出現警告,能夠肯定是您剛剛把它添加進來——以幫助查看那些可能不安全的代碼.隨着泛型的添加,它使用起來將更趁手.
Deprecated
遺憾的是,Deprecated沒那麼有用.它原本旨在替換@deprecated javadoc標籤,可是因爲它不包含任何字段,因此也就沒有方法來建議deprecated類或方法的用戶應該使用什麼作爲替代品.大多數用法都同時須要javadoc標籤和這個註釋.
Override
Override表示,它所註釋的方法應該重寫超類中具備相同簽名的方法:
@Override
public int hashCode() {
...
}
看上面的例子,若是沒有在hashCode中將"C"大寫,在編譯時不會出現錯誤,可是在運行時將沒法像指望的那樣調用該方法.經過添加Override標籤,編譯器會提示它是否真正地執行了重寫.
在超類發生改變的狀況中,這也頗有幫助.若是向該方法中添加一個新參數,並且方法自己也被重命名了,那麼子類將忽然不能編譯,由於它再也不重寫超類的任何東西.
其它註釋
註釋在其餘場景中很是有用.當不是直接修改行爲而是加強行爲時,特別是在添加樣板代碼的狀況下,註釋在諸如EJB和Web services這樣的框架中運行得很是好.
註釋不能用作預處理器.Sun的設計特別預防了徹底由於註釋而修改類的字節碼.這樣能夠正確地理解該語言的成果,並且IDE之類的工具也能夠執行深刻的代碼分析和重構之類的功能.
註釋不是銀彈.第一次遇到的時候,人們試圖嘗試各類技巧.請看下面這個從別人那裏得到的建議:
public class Foo {
@Property
private int bar;
}
其思想是爲私有字段bar自動建立getter和setter方法.遺憾的是,這個想法有兩個失敗之處:1)它不能運行,2)它使代碼難以閱讀和處理. 它是沒法實現的,由於前面已經提到了,Sun特別阻止了對出現註釋的類進行修改.
即便是可能的,它也不是一個好主意,由於它使代碼可讀性差.第一次看到這段代碼的人會不知道該註釋建立了方法.此外,若是未來您須要在這些方法內部執行一些操做,註釋也是沒用的. 總之,不要試圖用註釋去作那些常規代碼能夠完成的事情.
枚舉
enum很是像public static final int聲明,後者做爲枚舉值已經使用了不少年.對int所作的最大也是最明顯的改進是類型安全——您不能錯誤地用枚舉的一種類型代替另外一種類型,這一點和int不一樣,全部的int對編譯器來講都是同樣的.除去極少數例外的狀況,一般都應該用enum實例替換所有的枚舉風格的int結構.
枚舉提供了一些附加的特性.EnumMap和EnumSet這兩個實用類是專門爲枚舉優化的標準集合實現.若是知道集合只包含枚舉類型,那麼應該使用這些專門的集合來代替HashMap或HashSet.
大部分狀況下,可使用enum對代碼中的全部public static final int作插入替換.它們是可比的,而且能夠靜態導入,因此對它們的引用看起來是等同的,即便是對於內部類(或內部枚舉類型).注意,比較枚舉類型的時候,聲明它們的指令代表了它們的順序值.
"隱藏的"靜態方法
兩個靜態方法出如今全部枚舉類型聲明中.由於它們是枚舉子類上的靜態方法,而不是Enum自己的方法,因此它們在java.lang.Enum的javadoc中沒有出現.
第一個是values(),返回一個枚舉類型全部可能值的數組.
第二個是valueOf(),爲提供的字符串返回一個枚舉類型,該枚舉類型必須精確地匹配源代碼聲明.
方法
關於枚舉類型,咱們最喜歡的一個方面是它能夠有方法.過去您可能須要編寫一些代碼,對public static final int進行轉換,把它從數據庫類型轉換爲JDBC URL.而如今則可讓枚舉類型自己帶一個整理代碼的方法.下面就是一個例子,包括DatabaseType枚舉類型的抽象方法以及每一個枚舉實例中提供的實現:
public enum DatabaseType {
ORACLE {
public String getJdbcUrl() {...}
},
MYSQL {
public String getJdbcUrl() {...}
};
public abstract String getJdbcUrl();
}
如今枚舉類型能夠直接提供它的實用方法.例如:
DatabaseType dbType = ...;
String jdbcURL = dbType.getJdbcUrl();
要獲取URL,必須預先知道該實用方法在哪裏.
可變參數(Vararg)
正確地使用可變參數確實能夠清理一些垃圾代碼.典型的例子是一個帶有可變的String參數個數的log方法:
Log.log(String code)
Log.log(String code, String arg)
Log.log(String code, String arg1, String arg2)
Log.log(String code, String[] args)
當討論可變參數時,比較有趣的是,若是用新的可變參數替換前四個例子,將是兼容的:
Log.log(String code, String... args)
全部的可變參數都是源兼容的——那就是說,若是從新編譯log()方法的全部調用程序,能夠直接替換所有的四個方法.然而,若是須要向後的二進制兼容性,那麼就須要捨去前三個方法.只有最後那個帶一個字符串數組參數的方法等效於可變參數版本,所以能夠被可變參數版本替換.
類型強制轉換
若是但願調用程序瞭解應該使用哪一種類型的參數,那麼應該避免用可變參數進行類型強制轉換.看下面這個例子,第一項但願是String,第二項但願是Exception:
Log.log(Object... objects) {
String message = (String)objects[0];
if (objects.length > 1) {
Exception e = (Exception)objects[1];
// Do something with the exception
}
}
方法簽名應該以下所示,相應的可變參數分別使用String和Exception聲明:
Log.log(String message, Exception e, Object... objects) {...}
不要使用可變參數破壞類型系統.須要強類型化時纔可使用它.對於這個規則,PrintStream.printf()是一個有趣的例外:它提供類型信息做爲本身的第一個參數,以便稍後能夠接受那些類型.
協變返回
協變返回的基本用法是用於在已知一個實現的返回類型比API更具體的時候避免進行類型強制轉換.在下面這個例子中,有一個返回Animal對象的Zoo接口.咱們的實現返回一個AnimalImpl對象,可是在JDK 1.5以前,要返回一個Animal對象就必須聲明.:
public interface Zoo {
public Animal getAnimal();
}
public class ZooImpl implements Zoo {
public Animal getAnimal(){
return new AnimalImpl();
}
}
協變返回的使用替換了三個反模式:
直接字段訪問.爲了規避API限制,一些實現把子類直接暴露爲字段:
ZooImpl._animal
另外一種形式是,在知道實現的其實是特定的子類的狀況下,在調用程序中執行向下轉換:
((AnimalImpl)ZooImpl.getAnimal()).implMethod();
我看到的最後一種形式是一個具體的方法,該方法用來避免由一個徹底不一樣的簽名所引起的問題:
ZooImpl._getAnimal();
這三種模式都有它們的問題和侷限性.要麼是不夠整潔,要麼就是暴露了沒必要要的實現細節.
協變
協變返回模式就比較整潔、安全而且易於維護,它也不須要類型強制轉換或特定的方法或字段:
public AnimalImpl getAnimal(){
return new AnimalImpl();
}
使用結果:
ZooImpl.getAnimal().implMethod();
使用泛型
咱們將從兩個角度來了解泛型:使用泛型和構造泛型.咱們不討論List、Set和Map的顯而易見的用法.知道泛型集合是強大的而且應該常用就足夠了.
咱們將討論泛型方法的使用以及編譯器推斷類型的方法.一般這些都不會出問題,可是當出問題時,錯誤信息會很是使人費解,因此須要瞭解如何修復這些問題.
泛型方法
除了泛型類型,Java 5還引入了泛型方法.在這個來自java.util.Collections的例子中,構造了一個單元素列表.新的List的元素類型是根據傳入方法的對象的類型來推斷的:
static List Collections.singletonList(T o)
示例用法:
public List getListOfOne() {
return Collections.singletonList(1);
}
在示例用法中,咱們傳入了一個int.因此方法的返回類型就是List.編譯器把T推斷爲Integer.這和泛型類型是不一樣的,由於您一般不須要顯式地指定類型參數.
這也顯示了自動裝箱和泛型的相互做用.類型參數必須是引用類型:這就是爲何咱們獲得的是List而不是List.
不帶參數的泛型方法
emptyList()方法與泛型一塊兒引入,做爲java.util.Collections中EMPTY_LIST字段的類型安全置換:
static List Collections.emptyList()
示例用法:
public List getNoIntegers() {
return Collections.emptyList();
}
與先前的例子不一樣,這個方法沒有參數,那麼編譯器如何推斷T的類型呢?基本上,它將嘗試使用一次參數.若是沒有起做用,它再次嘗試使用返回或賦值類型.在本例中,返回的是List,因此T被推斷爲Integer.
若是在返回語句或賦值語句以外的位置調用泛型方法會怎麼樣呢?那麼編譯器將沒法執行類型推斷的第二次傳送.在下面這個例子中,emptyList()是從條件運算符內部調用的:
public List getNoIntegers() {
return x ? Collections.emptyList() : null;
}
由於編譯器看不到返回上下文,也不能推斷T,因此它放棄並採用Object.您將看到一個錯誤消息,好比:"沒法將List轉換爲List."
爲了修復這個錯誤,應顯式地向方法調用傳遞類型參數.這樣,編譯器就不會試圖推斷類型參數,就能夠得到正確的結果:
return x ? Collections.emptyList() : null;
這種狀況常常發生的另外一個地方是在方法調用中.若是一個方法帶一個List參數,而且須要爲那個參數調用這個傳遞的emptyList(),那麼也須要使用這個語法.
集合以外
這裏有三個泛型類型的例子,它們不是集合,而是以一種新穎的方式使用泛型.這三個例子都來自標準的Java庫:
Class
Class在類的類型上被參數化了.這就使無需類型強制轉換而構造一個newInstance成爲可能.
Comparable
Comparable被實際的比較類型參數化.這就在compareTo()調用時提供了更強的類型化.例如,String實現Comparable.對除String以外的任何東西調用compareTo(),都會在編譯時失敗.
Enum>
Enum被枚舉類型參數化.一個名爲Color的枚舉類型將擴展Enum.getDeclaringClass()方法返回枚舉類型的類對象,在這個例子中就是一個Color對象.它與getClass()不一樣,後者可能返回一個無名類.
通配符
泛型最複雜的部分是對通配符的理解.咱們將討論三種類型的通配符以及它們的用途.
首先讓咱們瞭解一下數組是如何工做的.能夠從一個Integer[]爲一個Number[]賦值.若是嘗試把一個Float寫到Number[]中,那麼能夠編譯,但在運行時會失敗,出現一個ArrayStoreException:
Integer[] ia = new Integer[5];
Number[] na = ia;
na[0] = 0.5; // compiles, but fails at runtime
若是試圖把該例直接轉換成泛型,那麼會在編譯時失敗,由於賦值是不被容許的:
List iList = new ArrayList();
List nList = iList; // not allowed
nList.add(0.5);
若是使用泛型,只要代碼在編譯時沒有出現警告,就不會遇到運行時ClassCastException.
上限通配符
咱們想要的是一個確切元素類型未知的列表,這一點與數組是不一樣的.
List是一個列表,其元素類型是具體類型Number.
List是一個確切元素類型未知的列表.它是Number或其子類型.
上限
若是咱們更新初始的例子,並賦值給List,那麼如今賦值就會成功了:
List iList = new ArrayList();
List nList = iList;
Number n = nList.get(0);
nList.add(0.5); // Not allowed
咱們能夠從列表中獲得Number,由於不管列表的確切元素類型是什麼(Float、Integer或Number),咱們均可以把它賦值給Number.
咱們仍然不能把浮點類型插入列表中.這會在編譯時失敗,由於咱們不能證實這是安全的.若是咱們想要向列表中添加浮點類型,它將破壞iList的初始類型安全——它只存儲Integer.
通配符給了咱們比數組更多的表達能力.
爲何使用通配符
在下面這個例子中,通配符用於向API的用戶隱藏類型信息.在內部,Set被存儲爲CustomerImpl.而API的用戶只知道他們正在獲取一個Set,從中能夠讀取Customer.
此處通配符是必需的,由於沒法從Set向Set賦值:
public class CustomerFactory {
private Set _customers;
public Set getCustomers() {
return _customers;
}
}
通配符和協變返回
通配符的另外一種常見用法是和協變返回一塊兒使用.與賦值相同的規則能夠應用到協變返回上.若是但願在重寫的方法中返回一個更具體的泛型類型,聲明的方法必須使用通配符:
public interface NumberGenerator {
public List generate();
}
public class FibonacciGenerator extends NumberGenerator {
public List generate() {
...
}
}
若是要使用數組,接口能夠返回Number[],而實現能夠返回Integer[].
下限
咱們所談的主要是關於上限通配符的.還有一個下限通配符.List是一個確切"元素類型"未知的列表,可是多是Mnumber,或者Number的超類型.因此它多是一個List或一個List.
下限通配符遠沒有上限通配符那樣常見,可是當須要它們的時候,它們就是必需的.
下限與上限
List readList = new ArrayList();
Number n = readList.get(0);
List writeList = new ArrayList();
writeList.add(new Integer(5));
第一個是能夠從中讀數的列表.
第二個是能夠向其寫數的列表.
無界通配符
最後,List列表的內容能夠是任何類型,並且它與List幾乎相同.能夠隨時讀取Object,可是不能向列表中寫入內容.
公共API中的通配符
總之,正如前面所說,通配符在向調用程序隱藏實現細節方面是很是重要的,但即便下限通配符看起來是提供只讀訪問,因爲remove(int position)之類的非泛型方法,它們也並不是如此.若是您想要一個真正不變的集合,可使用java.util.Collection上的方法,好比unmodifiableList().
編寫API的時候要記得通配符.一般,在傳遞泛型類型時,應該嘗試使用通配符.它使更多的調用程序能夠訪問API.
經過接收List而不是List,下面的方法能夠由許多不一樣類型的列表調用:
void removeNegatives(List list);
構造泛型類型
如今咱們將討論構造本身的泛型類型.咱們將展現一些例子,其中經過使用泛型能夠提升類型安全性,咱們還將討論一些實現泛型類型時的常見問題.
集合風格(Collection-like)的函數
第一個泛型類的例子是一個集合風格的例子.Pair有兩個類型參數,並且字段是類型的實例:
public final class Pair<a,b> {
public final A first;
public final B second;
public Pair(A first, B second) {
this.first = first;
this.second = second;
}
}
這使從方法返回兩個項而無需爲每一個兩種類型的組合編寫專用的類成爲可能.另外一種方法是返回Object[],而這樣是類型不安全或者不整潔的.
在下面的用法中,咱們從方法返回一個File和一個Boolean.方法的客戶端能夠直接使用字段而無需類型強制轉換:
public Pair<file,boolean> getFileAndWriteStatus(String path){
// create file and status
return new Pair<file,boolean>(file, status);
}
Pair<file,boolean> result = getFileAndWriteStatus("...");
File f = result.first;
boolean writeable = result.second;
集合以外
在下面這個例子中,泛型被用於附加的編譯時安全性.經過把DBFactory類參數化爲所建立的Peer類型,您其實是在強制Factory子類返回一個Peer的特定子類型:
public abstract class DBFactory {
protected abstract T createEmptyPeer();
public List get(String constraint) {
List peers = new ArrayList();
// database magic
return peers;
}
}
經過實現DBFactory,CustomerFactory必須從createEmptyPeer()返回一個Customer:
public class CustomerFactory extends DBFactory{
public Customer createEmptyPeer() {
return new Customer();
}
}
泛型方法
無論想要對參數之間仍是參數與返回類型之間的泛型類型施加約束,均可以使用泛型方法:
例如,若是編寫的反轉函數是在位置上反轉,那麼可能不須要泛型方法.然而,若是但願反轉返回一個新的List,那麼可能會但願新List的元素類型與傳入的List的類型相同.在這種狀況下,就須要一個泛型方法:
List reverse(List list)
具體化
當實現一個泛型類時,您可能想要構造一個數組T[].由於泛型是經過擦除(erasure)實現的,因此這是不容許的.
您能夠嘗試把Object[]強制轉換爲T[].但這是不安全的.
具體化解決方案
按照泛型教程的慣例,解決方案使用的是"類型令牌",經過向構造函數添加一個Class參數,能夠強制客戶端爲類的類型參數提供正確的類對象:
public class ArrayExample {
private Class clazz;
public ArrayExample(Class clazz) {
this.clazz = clazz;
}
public T[] getArray(int size) {
return (T[])Array.newInstance(clazz, size);
}
} 爲了構造ArrayExample,客戶端必須把String.class傳遞給構造函數,由於String.class的類型是Class. 擁有類對象使構造一個具備正確元素類型的數組成爲可能