文章詳情頁(yè)
體驗(yàn) JAVA 5 的新增語言特性
瀏覽:2日期:2024-06-16 11:20:37
內(nèi)容: Java 5.0發(fā)布了,許多人都將開始使用這個(gè)JDK版本的一些新增特性。從增強(qiáng)的for循環(huán)到諸如泛型(generic)之類更復(fù)雜的特性,都將很快出現(xiàn)在您所編寫的代碼中。我們剛剛完成了一個(gè)基于Java 5.0的大型任務(wù),而本文就是要介紹我們使用這些新特性的體驗(yàn)。本文不是一篇入門性的文章,而是對(duì)這些特性以及它們所產(chǎn)生的影響的深入介紹,同時(shí)還給出了一些在項(xiàng)目中更有效地使用這些特性的技巧。 簡(jiǎn)介在JDK 1.5的beta階段,我們?yōu)锽EA的Java IDE開發(fā)了一個(gè)Java 5編譯器。因?yàn)槲覀儗?shí)現(xiàn)了許多新特性,所以人們開始以新的方式利用它們;有些用法很聰明,而有些用法明顯應(yīng)該被列入禁用清單。編譯器本身使用了新的語言特性,所以我們也獲得了使用這些特性維護(hù)代碼的直接體驗(yàn)。本文將介紹其中的許多特性和使用它們的體驗(yàn)。 我們假定您已經(jīng)熟悉了這些新特性,所以不再全面介紹每個(gè)特性,而是談?wù)撘恍┯腥さ摹⒌芸赡懿惶黠@的內(nèi)容和用法。這些技巧出自我們的實(shí)際體驗(yàn),并大致按照語言特性進(jìn)行了分類。 我們將從最簡(jiǎn)單的特性開始,逐步過渡到高級(jí)特性。泛型所包含的內(nèi)容特別豐富,因此占了本文一半的篇幅。增強(qiáng)的for循環(huán)為了迭代集合和數(shù)組,增強(qiáng)的for循環(huán)提供了一個(gè)簡(jiǎn)單、兼容的語法。有兩點(diǎn)值得一提: Init表達(dá)式在循環(huán)中,初始化表達(dá)式只計(jì)算一次。這意味著您通常可以移除一個(gè)變量聲明。在這個(gè)例子中,我們必須創(chuàng)建一個(gè)整型數(shù)組來保存computeNumbers()的結(jié)果,以防止每一次循環(huán)都重新計(jì)算該方法。您可以看到,下面的代碼要比上面的代碼整潔一些,并且沒有泄露變量numbers: 未增強(qiáng)的For:int sum = 0;Integer[] numbers = computeNumbers();for (int i=0; i < numbers.length ; i++) sum += numbers[i];增強(qiáng)后的For: int sum = 0;for ( int number: computeNumbers() ) sum += number;局限性有時(shí)需要在迭代期間訪問迭代器或下標(biāo),看起來增強(qiáng)的for循環(huán)應(yīng)該允許該操作,但事實(shí)上不是這樣,請(qǐng)看下面的例子: for (int i=0; i < numbers.length ; i++) { if (i != 0) System.out.print(','); System.out.print(numbers[i]);}我們希望將數(shù)組中的值打印為一個(gè)用逗號(hào)分隔的清單。我們需要知道目前是否是第一項(xiàng),以便確定是否應(yīng)該打印逗號(hào)。使用增強(qiáng)的for循環(huán)是無法獲知這種信息的。我們需要自己保留一個(gè)下標(biāo)或一個(gè)布爾值來指示是否經(jīng)過了第一項(xiàng)。 這是另一個(gè)例子: for (Iterator it = n.iterator() ; it.hasNext() ; ) if (it.next() < 0) it.remove();在此例中,我們想從整數(shù)集合中刪除負(fù)數(shù)項(xiàng)。為此,需要對(duì)迭代器調(diào)用一個(gè)方法,但是當(dāng)使用增強(qiáng)的for 循環(huán)時(shí),迭代器對(duì)我們來說是看不到的。因此,我們只能使用Java 5之前版本的迭代方法。 順便說一下,這里需要注意的是,由于Iterator是泛型,所以其聲明是Iterator。許多人都忘記了這一點(diǎn)而使用了Iterator的原始格式。 注釋注釋處理是一個(gè)很大的話題。因?yàn)楸疚闹魂P(guān)注核心的語言特性,所以我們不打算涵蓋它所有的可能形式和陷阱。我們將討論內(nèi)置的注釋(SuppressWarnings,Deprecated和Override)以及一般注釋處理的局限性。 Suppress Warnings該注釋關(guān)閉了類或方法級(jí)別的編譯器警告。有時(shí)候您比編譯器更清楚地知道,代碼必須使用一個(gè)被否決的方法或執(zhí)行一些無法靜態(tài)確定是否類型安全的動(dòng)作,而使用:@SuppressWarnings('deprecation')public static void selfDestruct() { Thread.currentThread().stop();}這可能是內(nèi)置注釋最有用的地方。遺憾的是,1.5.0_04的javac不支持它。但是1.6支持它,并且Sun正在努力將其向后移植到1.5中。 Eclipse 3.1中支持該注釋,其他IDE也可能支持它。這允許您把代碼徹底地從警告中解脫出來。如果在編譯時(shí)出現(xiàn)警告,可以確定是您剛剛把它添加進(jìn)來——以幫助查看那些可能不安全的代碼。隨著泛型的添加,它使用起來將更趁手。 Deprecated遺憾的是,Deprecated沒那么有用。它本來旨在替換@deprecated javadoc標(biāo)簽,但是由于它不包含任何字段,所以也就沒有方法來建議deprecated類或方法的用戶應(yīng)該使用什么做為替代品。大多數(shù)用法都同時(shí)需要javadoc標(biāo)簽和這個(gè)注釋。 Override Override表示,它所注釋的方法應(yīng)該重寫超類中具有相同簽名的方法: @Overridepublic int hashCode() { ...}看上面的例子,如果沒有在hashCode中將“C大寫,在編譯時(shí)不會(huì)出現(xiàn)錯(cuò)誤,但是在運(yùn)行時(shí)將無法像期望的那樣調(diào)用該方法。通過添加Override標(biāo)簽,編譯器會(huì)提示它是否真正地執(zhí)行了重寫。 在超類發(fā)生改變的情況中,這也很有幫助。如果向該方法中添加一個(gè)新參數(shù),而且方法本身也被重命名了,那么子類將突然不能編譯,因?yàn)樗辉僦貙懗惖娜魏螙|西。 其它注釋注釋在其他場(chǎng)景中非常有用。當(dāng)不是直接修改行為而是增強(qiáng)行為時(shí),特別是在添加樣板代碼的情況下,注釋在諸如EJB和Web services這樣的框架中運(yùn)行得非常好。 注釋不能用做預(yù)處理器。Sun的設(shè)計(jì)特別預(yù)防了完全因?yàn)樽⑨尪薷念惖淖止?jié)碼。這樣可以正確地理解該語言的成果,而且IDE之類的工具也可以執(zhí)行深入的代碼分析和重構(gòu)之類的功能。 注釋不是銀彈。第一次遇到的時(shí)候,人們?cè)噲D嘗試各種技巧。請(qǐng)看下面這個(gè)從別人那里獲得的建議: public class Foo { @Property private int bar; }其思想是為私有字段bar自動(dòng)創(chuàng)建getter和setter方法。遺憾的是,這個(gè)想法有兩個(gè)失敗之處:1)它不能運(yùn)行,2)它使代碼難以閱讀和處理。 它是無法實(shí)現(xiàn)的,因?yàn)榍懊嬉呀?jīng)提到了,Sun特別阻止了對(duì)出現(xiàn)注釋的類進(jìn)行修改。 即使是可能的,它也不是一個(gè)好主意,因?yàn)樗勾a可讀性差。第一次看到這段代碼的人會(huì)不知道該注釋創(chuàng)建了方法。此外,如果將來您需要在這些方法內(nèi)部執(zhí)行一些操作,注釋也是沒用的。 總之,不要試圖用注釋去做那些常規(guī)代碼可以完成的事情。 枚舉 enum非常像public static final int聲明,后者作為枚舉值已經(jīng)使用了很多年。對(duì)int所做的最大也是最明顯的改進(jìn)是類型安全——您不能錯(cuò)誤地用枚舉的一種類型代替另一種類型,這一點(diǎn)和int不同,所有的int對(duì)編譯器來說都是一樣的。除去極少數(shù)例外的情況,通常都應(yīng)該用enum實(shí)例替換全部的枚舉風(fēng)格的int結(jié)構(gòu)。 枚舉提供了一些附加的特性。EnumMap和EnumSet這兩個(gè)實(shí)用類是專門為枚舉優(yōu)化的標(biāo)準(zhǔn)集合實(shí)現(xiàn)。如果知道集合只包含枚舉類型,那么應(yīng)該使用這些專門的集合來代替HashMap或HashSet。 大部分情況下,可以使用enum對(duì)代碼中的所有public static final int做插入替換。它們是可比的,并且可以靜態(tài)導(dǎo)入,所以對(duì)它們的引用看起來是等同的,即使是對(duì)于內(nèi)部類(或內(nèi)部枚舉類型)。注意,比較枚舉類型的時(shí)候,聲明它們的指令表明了它們的順序值。 “隱藏的靜態(tài)方法 兩個(gè)靜態(tài)方法出現(xiàn)在所有枚舉類型聲明中。因?yàn)樗鼈兪敲杜e子類上的靜態(tài)方法,而不是Enum本身的方法,所以它們?cè)趈ava.lang.Enum的javadoc中沒有出現(xiàn)。 第一個(gè)是values(),返回一個(gè)枚舉類型所有可能值的數(shù)組。 第二個(gè)是valueOf(),為提供的字符串返回一個(gè)枚舉類型,該枚舉類型必須精確地匹配源代碼聲明。 方法關(guān)于枚舉類型,我們最喜歡的一個(gè)方面是它可以有方法。過去您可能需要編寫一些代碼,對(duì)public static final int進(jìn)行轉(zhuǎn)換,把它從數(shù)據(jù)庫(kù)類型轉(zhuǎn)換為JDBC URL。而現(xiàn)在則可以讓枚舉類型本身帶一個(gè)整理代碼的方法。下面就是一個(gè)例子,包括DatabaseType枚舉類型的抽象方法以及每個(gè)枚舉實(shí)例中提供的實(shí)現(xiàn): public enum DatabaseType { ORACLE { public String getJdbcUrl() {...} }, MYSQL { public String getJdbcUrl() {...} }; public abstract String getJdbcUrl(); }現(xiàn)在枚舉類型可以直接提供它的實(shí)用方法。例如:DatabaseType dbType = ...;String jdbcURL = dbType.getJdbcUrl();要獲取URL,必須預(yù)先知道該實(shí)用方法在哪里。 可變參數(shù)(Vararg)正確地使用可變參數(shù)確實(shí)可以清理一些垃圾代碼。典型的例子是一個(gè)帶有可變的String參數(shù)個(gè)數(shù)的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)當(dāng)討論可變參數(shù)時(shí),比較有趣的是,如果用新的可變參數(shù)替換前四個(gè)例子,將是兼容的: Log.log(String code, String... args)所有的可變參數(shù)都是源兼容的——那就是說,如果重新編譯log()方法的所有調(diào)用程序,可以直接替換全部的四個(gè)方法。然而,如果需要向后的二進(jìn)制兼容性,那么就需要舍去前三個(gè)方法。只有最后那個(gè)帶一個(gè)字符串?dāng)?shù)組參數(shù)的方法等效于可變參數(shù)版本,因此可以被可變參數(shù)版本替換。 類型強(qiáng)制轉(zhuǎn)換 如果希望調(diào)用程序了解應(yīng)該使用哪種類型的參數(shù),那么應(yīng)該避免用可變參數(shù)進(jìn)行類型強(qiáng)制轉(zhuǎn)換。看下面這個(gè)例子,第一項(xiàng)希望是String,第二項(xiàng)希望是Exception: Log.log(Object... objects) { String message = (String)objects[0]; if (objects.length> 1) { Exception e = (Exception)objects[1]; // Do something with the exception } }方法簽名應(yīng)該如下所示,相應(yīng)的可變參數(shù)分別使用String和Exception聲明: Log.log(String message, Exception e, Object... objects) {...}不要使用可變參數(shù)破壞類型系統(tǒng)。需要強(qiáng)類型化時(shí)才可以使用它。對(duì)于這個(gè)規(guī)則,PrintStream.printf()是一個(gè)有趣的例外:它提供類型信息作為自己的第一個(gè)參數(shù),以便稍后可以接受那些類型。 協(xié)變返回 協(xié)變返回的基本用法是用于在已知一個(gè)實(shí)現(xiàn)的返回類型比API更具體的時(shí)候避免進(jìn)行類型強(qiáng)制轉(zhuǎn)換。在下面這個(gè)例子中,有一個(gè)返回Animal對(duì)象的Zoo接口。我們的實(shí)現(xiàn)返回一個(gè)AnimalImpl對(duì)象,但是在JDK 1.5之前,要返回一個(gè)Animal對(duì)象就必須聲明。: public interface Zoo { public Animal getAnimal(); } public class ZooImpl implements Zoo { public Animal getAnimal(){ return new AnimalImpl(); } }協(xié)變返回的使用替換了三個(gè)反模式: 直接字段訪問。為了規(guī)避API限制,一些實(shí)現(xiàn)把子類直接暴露為字段: ZooImpl._animal另一種形式是,在知道實(shí)現(xiàn)的實(shí)際上是特定的子類的情況下,在調(diào)用程序中執(zhí)行向下轉(zhuǎn)換: ((AnimalImpl)ZooImpl.getAnimal()).implMethod();我看到的最后一種形式是一個(gè)具體的方法,該方法用來避免由一個(gè)完全不同的簽名所引發(fā)的問題: ZooImpl._getAnimal();這三種模式都有它們的問題和局限性。要么是不夠整潔,要么就是暴露了不必要的實(shí)現(xiàn)細(xì)節(jié)。 協(xié)變 協(xié)變返回模式就比較整潔、安全并且易于維護(hù),它也不需要類型強(qiáng)制轉(zhuǎn)換或特定的方法或字段: public AnimalImpl getAnimal(){return new AnimalImpl();}使用結(jié)果: ZooImpl.getAnimal().implMethod();使用泛型我們將從兩個(gè)角度來了解泛型:使用泛型和構(gòu)造泛型。我們不討論List、Set和Map的顯而易見的用法。知道泛型集合是強(qiáng)大的并且應(yīng)該經(jīng)常使用就足夠了。 我們將討論泛型方法的使用以及編譯器推斷類型的方法。通常這些都不會(huì)出問題,但是當(dāng)出問題時(shí),錯(cuò)誤信息會(huì)非常令人費(fèi)解,所以需要了解如何修復(fù)這些問題。 泛型方法除了泛型類型,Java 5還引入了泛型方法。在這個(gè)來自java.util.Collections的例子中,構(gòu)造了一個(gè)單元素列表。新的List的元素類型是根據(jù)傳入方法的對(duì)象的類型來推斷的: static List Collections.singletonList(T o)示例用法:public List getListOfOne() { return Collections.singletonList(1);}在示例用法中,我們傳入了一個(gè)int。所以方法的返回類型就是List。編譯器把T推斷為Integer。這和泛型類型是不同的,因?yàn)槟ǔ2恍枰@式地指定類型參數(shù)。 這也顯示了自動(dòng)裝箱和泛型的相互作用。類型參數(shù)必須是引用類型:這就是為什么我們得到的是List而不是List。 不帶參數(shù)的泛型方法emptyList()方法與泛型一起引入,作為java.util.Collections中EMPTY_LIST字段的類型安全置換: static List Collections.emptyList()示例用法: public List getNoIntegers() { return Collections.emptyList();}與先前的例子不同,這個(gè)方法沒有參數(shù),那么編譯器如何推斷T的類型呢?基本上,它將嘗試使用一次參數(shù)。如果沒有起作用,它再次嘗試使用返回或賦值類型。在本例中,返回的是List,所以T被推斷為Integer。 如果在返回語句或賦值語句之外的位置調(diào)用泛型方法會(huì)怎么樣呢?那么編譯器將無法執(zhí)行類型推斷的第二次傳送。在下面這個(gè)例子中,emptyList()是從條件運(yùn)算符內(nèi)部調(diào)用的: public List getNoIntegers() { return x ? Collections.emptyList() : null;}因?yàn)榫幾g器看不到返回上下文,也不能推斷T,所以它放棄并采用Object。您將看到一個(gè)錯(cuò)誤消息,比如:“無法將List轉(zhuǎn)換為L(zhǎng)ist。 為了修復(fù)這個(gè)錯(cuò)誤,應(yīng)顯式地向方法調(diào)用傳遞類型參數(shù)。這樣,編譯器就不會(huì)試圖推斷類型參數(shù),就可以獲得正確的結(jié)果: return x ? Collections.emptyList() : null;這種情況經(jīng)常發(fā)生的另一個(gè)地方是在方法調(diào)用中。如果一個(gè)方法帶一個(gè)List參數(shù),并且需要為那個(gè)參數(shù)調(diào)用這個(gè)傳遞的emptyList(),那么也需要使用這個(gè)語法。 集合之外這里有三個(gè)泛型類型的例子,它們不是集合,而是以一種新穎的方式使用泛型。這三個(gè)例子都來自標(biāo)準(zhǔn)的Java庫(kù): ClassClass在類的類型上被參數(shù)化了。這就使無需類型強(qiáng)制轉(zhuǎn)換而構(gòu)造一個(gè)newInstance成為可能。 ComparableComparable被實(shí)際的比較類型參數(shù)化。這就在compareTo()調(diào)用時(shí)提供了更強(qiáng)的類型化。例如,String實(shí)現(xiàn)Comparable。對(duì)除String之外的任何東西調(diào)用compareTo(),都會(huì)在編譯時(shí)失敗。 EnumEnum被枚舉類型參數(shù)化。一個(gè)名為Color的枚舉類型將擴(kuò)展Enum。getDeclaringClass()方法返回枚舉類型的類對(duì)象,在這個(gè)例子中就是一個(gè)Color對(duì)象。它與getClass()不同,后者可能返回一個(gè)無名類。 通配符泛型最復(fù)雜的部分是對(duì)通配符的理解。我們將討論三種類型的通配符以及它們的用途。 首先讓我們了解一下數(shù)組是如何工作的。可以從一個(gè)Integer[]為一個(gè)Number[]賦值。如果嘗試把一個(gè)Float寫到Number[]中,那么可以編譯,但在運(yùn)行時(shí)會(huì)失敗,出現(xiàn)一個(gè)ArrayStoreException: Integer[] ia = new Integer[5];Number[] na = ia;na[0] = 0.5; // compiles, but fails at runtime如果試圖把該例直接轉(zhuǎn)換成泛型,那么會(huì)在編譯時(shí)失敗,因?yàn)橘x值是不被允許的:List iList = new ArrayList();List nList = iList; // not allowednList.add(0.5);如果使用泛型,只要代碼在編譯時(shí)沒有出現(xiàn)警告,就不會(huì)遇到運(yùn)行時(shí)ClassCastException。 上限通配符我們想要的是一個(gè)確切元素類型未知的列表,這一點(diǎn)與數(shù)組是不同的。 List是一個(gè)列表,其元素類型是具體類型Number。 List<? extends Number>是一個(gè)確切元素類型未知的列表。它是Number或其子類型。 上限如果我們更新初始的例子,并賦值給List<? extends Number>,那么現(xiàn)在賦值就會(huì)成功了: List iList = new ArrayList();List<? extends Number> nList = iList;Number n = nList.get(0);nList.add(0.5); // Not allowed我們可以從列表中得到Number,因?yàn)闊o論列表的確切元素類型是什么(Float、Integer或Number),我們都可以把它賦值給Number。 我們?nèi)匀徊荒馨迅↑c(diǎn)類型插入列表中。這會(huì)在編譯時(shí)失敗,因?yàn)槲覀儾荒茏C明這是安全的。如果我們想要向列表中添加浮點(diǎn)類型,它將破壞iList的初始類型安全——它只存儲(chǔ)Integer。 通配符給了我們比數(shù)組更多的表達(dá)能力。 為什么使用通配符在下面這個(gè)例子中,通配符用于向API的用戶隱藏類型信息。在內(nèi)部,Set被存儲(chǔ)為CustomerImpl。而API的用戶只知道他們正在獲取一個(gè)Set,從中可以讀取Customer。 此處通配符是必需的,因?yàn)闊o法從Set向Set賦值: public class CustomerFactory { private Set _customers; public Set<? extends Customer> getCustomers() { return _customers; }}通配符和協(xié)變返回通配符的另一種常見用法是和協(xié)變返回一起使用。與賦值相同的規(guī)則可以應(yīng)用到協(xié)變返回上。如果希望在重寫的方法中返回一個(gè)更具體的泛型類型,聲明的方法必須使用通配符: public interface NumberGenerator { public List<? extends Number> generate();}public class FibonacciGenerator extends NumberGenerator { public List generate() { ... }}如果要使用數(shù)組,接口可以返回Number[],而實(shí)現(xiàn)可以返回Integer[]。 下限 我們所談的主要是關(guān)于上限通配符的。還有一個(gè)下限通配符。List<? super Number>是一個(gè)確切“元素類型未知的列表,但是可能是Mnumber,或者Number的超類型。所以它可能是一個(gè)List或一個(gè)List。 下限通配符遠(yuǎn)沒有上限通配符那樣常見,但是當(dāng)需要它們的時(shí)候,它們就是必需的。 下限與上限List<? extends Number> readList = new ArrayList();Number n = readList.get(0);List<? super Number> writeList = new ArrayList();writeList.add(new Integer(5));第一個(gè)是可以從中讀數(shù)的列表。 第二個(gè)是可以向其寫數(shù)的列表。 無界通配符最后,List<?>列表的內(nèi)容可以是任何類型,而且它與List<? extends Object>幾乎相同。可以隨時(shí)讀取Object,但是不能向列表中寫入內(nèi)容。 公共API中的通配符 總之,正如前面所說,通配符在向調(diào)用程序隱藏實(shí)現(xiàn)細(xì)節(jié)方面是非常重要的,但即使下限通配符看起來是提供只讀訪問,由于remove(int position)之類的非泛型方法,它們也并非如此。如果您想要一個(gè)真正不變的集合,可以使用java.util.Collection上的方法,比如unmodifiableList()。 編寫API的時(shí)候要記得通配符。通常,在傳遞泛型類型時(shí),應(yīng)該嘗試使用通配符。它使更多的調(diào)用程序可以訪問API。 通過接收List<? extends Number>而不是List,下面的方法可以由許多不同類型的列表調(diào)用: void removeNegatives(List<? extends Number> list);構(gòu)造泛型類型現(xiàn)在我們將討論構(gòu)造自己的泛型類型。我們將展示一些例子,其中通過使用泛型可以提高類型安全性,我們還將討論一些實(shí)現(xiàn)泛型類型時(shí)的常見問題。集合風(fēng)格(Collection-like)的函數(shù)第一個(gè)泛型類的例子是一個(gè)集合風(fēng)格的例子。Pair有兩個(gè)類型參數(shù),而且字段是類型的實(shí)例: public final class Pair { public final A first; public final B second; public Pair(A first, B second) { this.first = first; this.second = second; }}這使從方法返回兩個(gè)項(xiàng)而無需為每個(gè)兩種類型的組合編寫專用的類成為可能。另一種方法是返回Object[],而這樣是類型不安全或者不整潔的。 在下面的用法中,我們從方法返回一個(gè)File和一個(gè)Boolean。方法的客戶端可以直接使用字段而無需類型強(qiáng)制轉(zhuǎn)換: public Pair getFileAndWriteStatus(String path){ // create file and status return new Pair(file, status);}Pair result = getFileAndWriteStatus('...');File f = result.first;boolean writeable = result.second;集合之外 在下面這個(gè)例子中,泛型被用于附加的編譯時(shí)安全性。通過把DBFactory類參數(shù)化為所創(chuàng)建的Peer類型,您實(shí)際上是在強(qiáng)制Factory子類返回一個(gè)Peer的特定子類型: public abstract class DBFactory { protected abstract T createEmptyPeer(); public List get(String constraint) { List peers = new ArrayList(); // database magic return peers; }}通過實(shí)現(xiàn)DBFactory,CustomerFactory必須從createEmptyPeer()返回一個(gè)Customer:public class CustomerFactory extends DBFactory{ public Customer createEmptyPeer() { return new Customer(); }}泛型方法不管想要對(duì)參數(shù)之間還是參數(shù)與返回類型之間的泛型類型施加約束,都可以使用泛型方法: 例如,如果編寫的反轉(zhuǎn)函數(shù)是在位置上反轉(zhuǎn),那么可能不需要泛型方法。然而,如果希望反轉(zhuǎn)返回一個(gè)新的List,那么可能會(huì)希望新List的元素類型與傳入的List的類型相同。在這種情況下,就需要一個(gè)泛型方法: List reverse(List list) 具體化當(dāng)實(shí)現(xiàn)一個(gè)泛型類時(shí),您可能想要構(gòu)造一個(gè)數(shù)組T[]。因?yàn)榉盒褪峭ㄟ^擦除(erasure)實(shí)現(xiàn)的,所以這是不允許的。 您可以嘗試把Object[]強(qiáng)制轉(zhuǎn)換為T[]。但這是不安全的。 具體化解決方案按照泛型教程的慣例,解決方案使用的是“類型令牌,通過向構(gòu)造函數(shù)添加一個(gè)Class參數(shù),可以強(qiáng)制客戶端為類的類型參數(shù)提供正確的類對(duì)象: public class ArrayExample { private Class clazz; public ArrayExample(Class clazz) { this.clazz = clazz; } public T[] getArray(int size) { return (T[])Array.newInstance(clazz, size); }}為了構(gòu)造ArrayExample,客戶端必須把String.class傳遞給構(gòu)造函數(shù),因?yàn)镾tring.class的類型是Class。 擁有類對(duì)象使構(gòu)造一個(gè)具有正確元素類型的數(shù)組成為可能。 結(jié)束語總而言之,新的語言特性有助于從根本上改變Java。通過了解在什么場(chǎng)景下使用以及如何使用這些新特性,您將會(huì)編寫出更好的代碼。補(bǔ)充閱讀 1.Enhancements in JDK 5——JDK 5中新特性的官方列表 2.Generics Tutorial (PDF)——Gilad Bracha的泛型教程 Java, java, J2SE, j2se, J2EE, j2ee, J2ME, j2me, ejb, ejb3, JBOSS, jboss, spring, hibernate, jdo, struts, webwork, ajax, AJAX, mysql, MySQL, Oracle, Weblogic, Websphere, scjp, scjd Java 5.0發(fā)布了,許多人都將開始使用這個(gè)JDK版本的一些新增特性。從增強(qiáng)的for循環(huán)到諸如泛型(generic)之類更復(fù)雜的特性,都將很快出現(xiàn)在您所編寫的代碼中。我們剛
標(biāo)簽:
Java
相關(guān)文章:
1. ASP.NET泛型三之使用協(xié)變和逆變實(shí)現(xiàn)類型轉(zhuǎn)換2. 一文透徹詳解.NET框架類型系統(tǒng)設(shè)計(jì)要點(diǎn)3. .NET 8新預(yù)覽版使用 Blazor 組件進(jìn)行服務(wù)器端呈現(xiàn)(項(xiàng)目體驗(yàn))4. JavaScrip簡(jiǎn)單數(shù)據(jù)類型隱式轉(zhuǎn)換的實(shí)現(xiàn)5. PHP基礎(chǔ)之?dāng)?shù)據(jù)類型2——整型(Integer)6. Python變量及數(shù)據(jù)類型用法原理匯總7. PHP中強(qiáng)制類型轉(zhuǎn)換的示例詳解8. 深度思考JDK8中日期類型該如何使用詳解9. Python動(dòng)態(tài)強(qiáng)類型解釋型語言原理解析10. PHP擴(kuò)展類型及安裝方式解析
排行榜
