熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

Java高級編程——泛型類型 第一部分

2013-11-23 19:19:37  來源: Java核心技術 

  編輯按《Java in a Nutshell th Edition》覆蓋了jdk中很多變化和新特征其中最重要的就是泛型在本文的第一部分作者David Flanagan介紹了如何使用泛型而在第二部分作者描述了如何寫你自己的泛型和泛型方法
  
  Java的新特性之一是引入了泛型類型和泛型方法一個泛型類型通過使用一個或多個類型變量來定義並擁有一個或多個使用一個類型變量作為一個參數或者返回值的占位符例如類型javautilList<E>是一個泛型類型一個list其元素的類型被占位符E描述這個類型有一個名為add()的方法被聲明為有一個類型為E的參數同時有一個get()方法返回值被聲明為E類型
  
  為了使用泛型類型你應該為類型變量詳細指明實際的類型形成一個就像List<String>類似的參數化類型[]指明這些額外的類型信息的原因是編譯器據此能夠在編譯期為您提供很強的類型檢查增強您的程序的類型安全性舉個例子來說您有一個只能保持String對象的List那麼這種類型檢查就能夠阻止您往裡面加入String[]對象同樣的增加的類型信息使編譯器能夠為您做一些類型轉換的事情比如編譯器知道了一個List<String>有個get()方法其返回值是一個String對象因此您不再需要去將返回值由一個Object強制轉換為String
  
  Javautil包中的集合類在java中已經被做成了泛型也許您將會在您的程序中頻繁的使用到他們類型安全的集合類就是一個泛型類型的典型案例即便您從沒有定義過您自己的泛型類型甚至從未用過除了javautil中的集合類以外的泛型類型類型安全的集合類的好處也是極有意義的一個標志——他們證明了這個主要的新語言特性的復雜性
  
  我們從探索類型安全的集合類中的基本的泛型用法開始進而研究更多使用泛型類型的復雜細節然後我們討論類型參數通配符和有界通配符描繪了如何使用泛型以後我們闡明如何編寫自己的泛型類型和泛型方法我們對於泛型的討論將結束於一趟對於JavaAPI的核心中重要的泛型類型的旅行這趟旅程將探索這些類型以及他們的用法旅程的目的是為了讓您對泛型如何工作這個問題有個深入的理解
  
  類型安全集合類
  
  Javautil類包包含了Java集合框架(Java Collections Framework)這是一批包含對象的set對象的list以及基於keyvalue的map第五章將談到集合類這裡我們討論的是在java中集合類使用類型參數來界定集合中的對象的類型這個討論並不適合java或更早期版本如果沒有泛型對於集合類的使用需要程序員記住每個集合中元素的類型當您在java種創建了一個集合您知道您放入到集合中的對象的類型但是編譯器不知道您必須小心地往其中加入一個合適類型的元素當需要從集合中獲取元素時您必須顯式的寫強制類型轉換以將他們從Object轉換為他們真是的類型考察下邊的java的代碼
  
  public static void main(String[] args) {  // This list is intended to hold only strings  // The compiler doesnt know that so we have to remember ourselves  List wordlist = new ArrayList();   // Oops! We added a String[] instead of a String  // The compiler doesnt know that this is an error  wordlistadd(args);  // Since List can hold arbitrary objects the get() method returns  // Object Since the list is intended to hold strings we cast the  // return value to String but get a ClassCastException because of  // the error above  String word = (String)wordlistget();}
  
  泛型類型解決了這段代碼中的顯示的類型安全問題Javautil中的List或是其他集合類已經使用泛型重寫過了就像前面提到的 List被重新定義為一個list它中間的元素類型被一個類型可變的名稱為E的占位符描述Add()方法被重新定義為期望一個類型為E的參數用於替換以前的Objectget()方法被重新定義為返回一個E替換了以前的Object
  
  在java當我們申明一個List或者創建一個ArrayList的實例的時候我們需要在泛型類型的名字後面緊跟一對<>尖括號中寫入我們需要的實際的類型比如一個保持String的List應該寫成List<String>需要注意的是這非常象給一個方法傳一個參數區別是我們使用類型而不是值同時使用尖括號而不是圓括號
  
  Javautil的集合類中的元素必須是對象化的他們不能是基本類型泛型的引入並沒有改變這點泛型不能使用基本類型我們不能這樣來申明——Set<char>或者List<int>記住無論如何java中的自動打包和自動解包特性使得使用Set<Character>或者List<Integer>和直接使用char和int值一樣方便(查看第二章以了解更多關於自動打包和自動解包的細節)
  
  在Java上面的例子將被重寫為如下方式
  
  public static void main(String[] args) {  // This list can only hold String objects  List<String> wordlist = new ArrayList<String>();  // args is a String[] not String so the compiler wont let us do this  wordlistadd(args); // Compilation error!  // We can do this though   // Notice the use of the new for/in looping statement  for(String arg : args) wordlistadd(arg);  // No cast is required List<String>get() returns a String  String word = wordlistget();}
  
  值得注意的是代碼量其實並沒有比原來那個沒有泛型的例子少多少使用(String)這樣的類型轉換被替換成了類型參數<String> 不同的是類型參數需要且僅需要聲明一次而list能夠被使用任何多次不需要類型轉換在更長點的例子代碼中這一點將更加明顯即使在那些看上去泛型語法比非泛型語法要冗長的例子裡使用泛型依然是非常有價值的——額外的類型信息允許編譯器在您的代碼裡執行更強的錯誤檢查以前只能在運行起才能發現的錯誤現在能夠在編譯時就被發現此外以前為了處理類型轉換的異常我們需要添加額外的代碼行如果沒有泛型那麼當發生類型轉換異常的時候一個ClassCastException異常就會被從實際代碼中拋出
  
  就像一個方法可以使用任意數量的參數一樣類允許使用多個類型變量接口JavautilMap就是一個例子一個Map體現了從一個key的對象到一個value的對象的映射關系接口Map申明了一個類型變量來描述key的類型而另一個類型變量來描述value的類型舉個例子來說假設您希望做一個String對象到Integer對象的映射關系
  
  public static void main(String[] args) {  // A map from strings to their position in the args[] array  Map<StringInteger> map = new HashMap<StringInteger>();  // Note that we use autoboxing to wrap i in an Integer object  for(int i=; i < argslength; i++) mapput(args[i] i);   // Find the array index of a word Note no cast is required!  Integer position = mapget(hello);  // We can also rely on autounboxing to convert directly to an int  // but this throws a NullPointerException if the key does not exist   // in the map  int pos = mapget(world);}
  
  象List<String>這個一個參數類型其本身也是也一個類型也能夠被用於當作其他類型的一個類型變量值您可能會看到這樣的代碼
  
  // Look at all those nested angle brackets!Map<String List<List<int[]>>> map = getWeirdMap();// The compiler knows all the types and we can write expressions// like this without casting We might still get NullPointerException// or ArrayIndexOutOfBounds at runtime of courseint value = mapget(key)get()get()[];// Heres how we break that expression down step by stepList<List<int[]>> listOfLists = mapget(key);List<int[]> listOfIntArrays = listOfListsget();int[] array = listOfIntArraysget();int element = array[];
  
  在上面的代碼裡javautilList<E>和javautilMap<KV>的get()方法返回一個類型為E的list元素或者一個類型為V的map元素注意無論如何泛型類型能夠更精密的使用他們的變量在本書中的參考章節查看List<E>您將會看到它的iterator( )方法被聲明為返回一個Iterator<E>這意味著這個方法返回一個跟list的實際的參數類型一樣的一個參數類型的實例為了具體的說明這點下面的例子提供了不使用get()方法來獲取一個List<String>的第一個元素的方法
  
  List<String> words = // initialized elsewhereIterator<String> iterator = erator();String firstword = iteratornext();
  
  理解泛型類型
  
  本段將對泛型類型的使用細節做進一步的探討以嘗試說明下列問題
  
  不帶類型參數的使用泛型的後果
  
  參數化類型的體系
  
  一個關於編譯期泛型類型的類型安全的漏洞和一個用於確保運行期類型安全的補丁
  
  為什麼參數化類型的數組不是類型安全的
  
  未經處理的類型和不被檢查的警告
  
  即使被重寫的Java集合類帶來了泛型的好處在使用他們的時候您也不被要求說明類型變量一個不帶類型變量的泛型類型被認為是一個未經處理的類型(raw type)這樣版本以前的jav
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26671.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.