java泛型學習和實踐(1),java泛
引言
JDK1.5之後引入了泛型,泛型剛開始接觸時比較難理解,經過慢慢學習,有一些收穫,現總結下,既可以加深自身理解,也可以協助他人。
由於本人水平有限,難免有不對的地方,還請指正。廢話不多說,下面正式開始。
為了更直觀的說明泛型,現假設一個司機開車的情境。我們需要3個類,Driver(司機)、Ford(福特)、Buick(別克)。
第一版
第一版本代碼如下:
public class Buick { public void run(){ System.out.println("buick run"); } public void autoRun(){ System.out.println("buick auto-run"); } }
public class Ford { public void run(){ System.out.println("ford run"); } public void fly(){ System.out.println("ford fly"); }}
public class Driver { private Object car; public void drive(Object car){ this.car = car; if(car instanceof Buick){ System.out.println("I am driving a " + car); ((Buick)car).run(); }else if(car instanceof Ford){ System.out.println("I am driving a " + car); ((Ford)car).run(); } } public Object getDrivingCar(){ return car; } public static void main(String[] args) { Driver driver1 = new Driver(); driver1.drive(new Ford()); Driver driver2 = new Driver(); driver2.drive(new Buick()); //...執行其它商務邏輯 /** 擷取司機1開的福特車執行fly方法 */ ((Ford)driver1.getDrivingCar()).fly(); /** 擷取司機2開的別克車執行autoRun方法 */ ((Buick)driver2.getDrivingCar()).autoRun(); }}
簡單說明下三個類。Buick類和Ford類都含有run方法,Buick類有autoRun方法,Ford類有fly方法。Driver類有一個drive方法和getDrivingCar方法及內部屬性car。main靜態方法是示範樣本。
通過上面的示範樣本可以發現,在調用Driver執行個體的drive方法時,實際參數向上轉型為Object對象,而在調用getDrivingCar方法時,又需要向下轉型為具體的car類來調用其特定的方法。那麼這種方式會有什麼問題呢?
問題是顯而易見的,drive方法的參數類型和getDrivingCar的返回值類型是一樣的,但由於要支援不同的car,向上轉型為Object類型(沒有定義為具體的Buick類型或Ford類型),這樣外部在調用getDrivingCar時,返回的是一般化的Object,這樣就失去了具體類的特徵,需要外部強制類型轉換。這種顯示強制轉換方式很容易產生ClassCastException異常(不小心把driver2寫成driver1時執行代碼時會拋出該異常),並且這種編程錯誤不能在編譯期間發現,而要等到運行期間才能發現,有可能產生隱藏BUG。並且這種方式也不合理,Driver類的使用門檻過高,需要外部代碼很小心的處理來防止出現錯誤,例如在強制轉換前用instanceof進行判斷。那麼有沒更好的方式呢?
第二版
第二版本代碼如下(只修改Driver類):
public class Driver<T> { private T car; public void drive(T car){ this.car = car; if(car instanceof Buick){ System.out.println("I am driving a " + car); ((Buick)car).run(); }else if(car instanceof Ford){ System.out.println("I am driving a " + car); ((Ford)car).run(); } } public T getDrivingCar(){ return car; } public static void main(String[] args) { Driver<Ford> driver1 = new Driver<Ford>(); driver1.drive(new Ford()); Driver<Buick> driver2 = new Driver<Buick>(); driver2.drive(new Buick()); //...執行其它商務邏輯 /** 擷取司機1開的福特車執行fly方法 */ driver1.getDrivingCar().fly(); /** 擷取司機2開的別克車執行autoRun方法 */ driver2.getDrivingCar().autoRun(); }}
Driver類聲明中使用的<T>,即泛型T。你可以把T類型理解成代表任意一種具體類型的類型。同時修改drive方法參數類型和getDrivingCar方法返回值類型及私人屬性car都是T類型。那麼這樣做有什麼好處呢?
從main方法的示範使用代碼可以看出,在執行個體化Driver類時,需要指定具體的T類型(driver1中的Ford和driver2中Buick)。執行個體化之後,調用getDrivingCar方法的傳回型別也是相應的具體類型,從而可以直接調用具體類型特定的方法。
比較第一版的Driver類,改進版的Driver類,只需要使用者在類執行個體化時指定具體的T類型,在後續操作中不需要進行強制類型轉換,從而減少出錯的可能性,提高了代碼的安全性。
通過泛型T,很輕鬆對drive方法參數類型、getDrivingCar方法返回值類型及私人屬性car類型這三處地方的類型進行了統一,且不失具體性(不像第一版將car申明為Object類型)不需要額外的代碼成本,類結構看上去清晰明了。對外部使用者,使用門檻也低,不會出現不小心弄錯類型的錯誤(想出現也不可能,如果指定了錯誤類型,會在編譯期間提示錯誤)
上面的樣本示範了泛型最基本的用法。通過泛型,很容易在類的成員變數或方法參數或方法返回值之間聲明為統一類型且不失類型的具體性(在類定義中是泛型,但在執行個體化時需要指定具體的類型)
聯絡實際
現在大家應該對泛型有了一個基本的瞭解。下面舉個執行個體項目中的例子,更進一步說明問題。
項目中一般都需要分頁類,簡化版如下
/** * <簡單分頁類> */public class SimplePage { /** 每頁顯示的記錄數*/ private int numPerPage; /** 總記錄數 */ private int totalCount; /** 當前頁 */ private int pageNum; //...省略其它方法}
/** * <帶內容分頁類> */public class Pageable<E> extends SimplePage { /** * 本頁內容 */ private List<E> list; /** * <設定內容> * @param list */ public void setList(List<E> list) { this.list = list; } /** * <擷取內容> * @return */ public List<E> getList() { return list; }}
SimplePage:簡單分頁類,包含當前頁,每頁顯示記錄數,總記錄數等基本成員屬性。
Pageable:業務資料分頁類,繼承自簡單分頁類,帶有本頁資料資訊。
比如要分頁查詢使用者資訊時,假設有User類封裝使用者資訊。使用代碼如下:
Pageable<User> pageInfo = new Pageable<User>(); //...分頁擷取出使用者資訊,並調用setList方法將使用者資訊放入pageInfo執行個體中 /** 其它地方擷取到userList,進行其它業務操作 */ List<User> userList = pageInfo.getList();
使用起來非常簡單,今天就碼到這裡了,未完待續。
碼字不易,轉載請標明出處