- Java高級(jí)特性編程及實(shí)戰(zhàn)
- 肖睿 龍浩 孫琳
- 3352字
- 2020-09-18 18:35:53
任務(wù)2 查詢標(biāo)題功能升級(jí)
關(guān)鍵步驟如下。
- 修改任務(wù)1,將集合改為泛型形式。
- 修改遍歷集合的代碼。
1.2.1 認(rèn)識(shí)泛型
泛型是JDK 1.5的新特性,泛型的本質(zhì)是參數(shù)化類型,也就是說所操作的數(shù)據(jù)類型被指定為一個(gè)參數(shù),使代碼可以應(yīng)用于多種類型。簡(jiǎn)單說來,Java語言引入泛型的好處是安全簡(jiǎn)單,且所有強(qiáng)制轉(zhuǎn)換都是自動(dòng)和隱式進(jìn)行的,提高了代碼的重用率。
1. 泛型的定義

泛型集合
將對(duì)象的類型作為參數(shù),指定到其他類或者方法上,從而保證類型轉(zhuǎn)換的安全性和穩(wěn)定性,這就是泛型。泛型的本質(zhì)就是參數(shù)化類型。
泛型的定義語法格式如下。
類1或者接口<類型實(shí)參> 對(duì)象=new類2<類型實(shí)參>();
注意
首先,“類2”可以是“類1”本身,可以是“類1”的子類,還可以是接口的實(shí)現(xiàn)類;其次,“類2”的類型實(shí)參必須與“類1”中的類型實(shí)參相同。
例如:ArrayList<String> list=new ArrayList<String>();
上述代碼表示創(chuàng)建一個(gè)ArrayList集合,但規(guī)定該集合中存儲(chǔ)的元素類型必須為String類型。
2. 泛型在集合中的應(yīng)用
前面學(xué)習(xí)List接口時(shí)已經(jīng)提到,其add()方法的參數(shù)是Object類型,不管把什么對(duì)象放入List接口及其子接口或?qū)崿F(xiàn)類中,都會(huì)被轉(zhuǎn)換為Object類型。在通過get()方法取出集合中元素時(shí)必須進(jìn)行強(qiáng)制類型轉(zhuǎn)換,不僅煩瑣而且容易出現(xiàn)ClassCastException異常。Map接口中使用put()方法和get()方法存取對(duì)象時(shí),以及使用Iterator的next()方法獲取元素時(shí)存在同樣問題。JDK 1.5中通過引入泛型有效地解決了這個(gè)問題。JDK 1.5中已經(jīng)改寫了集合框架中的所有接口和類,增加了對(duì)泛型的支持,也就是泛型集合。
使用泛型集合在創(chuàng)建集合對(duì)象時(shí)指定集合中元素的類型,從集合中取出元素時(shí)無需進(jìn)行強(qiáng)制類型轉(zhuǎn)換,并且如果把非指定類型對(duì)象放入集合,會(huì)出現(xiàn)編譯錯(cuò)誤。
List和ArrayList的泛型形式是List<E>和ArrayList<E>,ArrayList<E>與ArrayList類的常用方法基本一樣,示例11演示了List<E>和ArrayList<E>的用法。
示例11
使用ArrayList的泛型形式改進(jìn)示例2。
實(shí)現(xiàn)步驟如下。
(1)實(shí)現(xiàn)步驟同示例2。
(2)創(chuàng)建集合對(duì)象時(shí),使用的是ArrayList<NewTitle>。
(3)遍歷集合時(shí)不需要進(jìn)行類型轉(zhuǎn)換。
關(guān)鍵代碼:
//省略與示例2相同部分的代碼 List<NewTitle> newsTitleList=new ArrayList<NewTitle>(); //按照順序依次添加新聞標(biāo)題 newsTitleList.add(car); newsTitleList.add(test); //根據(jù)位置獲取相應(yīng)新聞標(biāo)題,逐條輸出每條新聞標(biāo)題的名稱 System.out.println("新聞標(biāo)題的名稱為:"); for (NewTitle title:newsTitleList) { System.out.println(title.getTitleName()); }
輸出結(jié)果與示例2相同,如圖1.3所示。
示例11中通過<NewTitle>指定了ArrayList中元素的類型,代碼中指定了ArrayList中只能添加NewTitle類型的數(shù)據(jù),如果添加其他類型數(shù)據(jù),將會(huì)出現(xiàn)編譯錯(cuò)誤,這在一定程度上保證了代碼安全性。并且數(shù)據(jù)添加到集合中后不再轉(zhuǎn)換為Object類型,保存的是指定的數(shù)據(jù)類型,所以在集合中獲取數(shù)據(jù)時(shí)也不再需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換。
同樣的,Map與HashMap也有它們的泛型形式,即Map<K,V>和HashMap<K,V>。因?yàn)樗鼈兊拿恳粋€(gè)元素都包含兩個(gè)部分,即key和value,所以,在應(yīng)用泛型時(shí),要同時(shí)指定key的類型和value的類型,K表示key的類型,V表示value的類型。
HashMap<K,V>操作數(shù)據(jù)的方法與HashMap基本一樣,示例12演示了Map<K,V>和HashMap<K,V>的用法。
示例12
使用HashMap的泛型形式改進(jìn)示例7。
實(shí)現(xiàn)步驟如下。
(1)實(shí)現(xiàn)步驟同示例7。
(2)創(chuàng)建集合對(duì)象時(shí),使用的是HashMap<String,Student>。
(3)遍歷集合時(shí)不需要進(jìn)行類型轉(zhuǎn)換。
關(guān)鍵代碼:
//省略與示例7相同部分的代碼 Map<String,Student> students=new HashMap<String,Student>(); //把英文名稱與學(xué)員對(duì)象按照“鍵——值對(duì)”的方式存儲(chǔ)在HashMap中 students.put("Jack", student1); students.put("Rose", student2); //輸出英文名 System.out.println("學(xué)生英文名:"); for(String key:students.keySet()){ System.out.println(key); } //輸出學(xué)生詳細(xì)信息 System.out.println("學(xué)生詳細(xì)信息:"); for(Student value:students.values()){ System.out.println("姓名:"+value.getName()+",性別:"+value.getSex()); }
輸出結(jié)果與示例7相同,如圖1.8所示。
在示例12中,通過<String,Student>指定了Map集合的數(shù)據(jù)類型,在使用put()方法存儲(chǔ)數(shù)據(jù)時(shí),Map集合的key必須為String類型,value必須為Student類型的數(shù)據(jù),而在遍歷鍵集的for循環(huán)中,變量key的類型不再是Object,而是String;在遍歷值集的for循環(huán)中,變量value的類型不再是Object,而是Student,同樣,Map.get(key)得到的值也是Student類型數(shù)據(jù),不再需要進(jìn)行強(qiáng)制類型轉(zhuǎn)換。
當(dāng)然,其他的集合類,如前面講到的LinkedList、HashSet等也都有自己的泛型形式,用法和ArrayList、HashMap的泛型形式類似,這里不再贅述。
泛型使集合的使用更方便,也提升了安全:
- 存儲(chǔ)數(shù)據(jù)時(shí)進(jìn)行嚴(yán)格類型檢查,確保只有合適類型的對(duì)象才能存儲(chǔ)在集合中。
- 從集合中檢索對(duì)象時(shí),減少了強(qiáng)制類型轉(zhuǎn)換。
1.2.2 深入泛型
在集合中使用泛型只是泛型多種應(yīng)用的一種,在接口、類、方法等方面也有著泛型的廣泛應(yīng)用。泛型的本質(zhì)就是參數(shù)化類型,參數(shù)化類型的重要性在于允許創(chuàng)建一些類、接口和方法,其所操作的數(shù)據(jù)類型被定義為參數(shù),可以在真正使用時(shí)指定具體的類型。
在學(xué)習(xí)如何使用泛型之前,還需要了解以下兩個(gè)重要的概念。
- 參數(shù)化類型:參數(shù)化類型包含一個(gè)類或者接口,以及實(shí)際的類型參數(shù)列表。
- 類型變量:是一種非限定性標(biāo)識(shí)符,用來指定類、接口或者方法的類型。
1. 定義泛型類、泛型接口和泛型方法
對(duì)于一些常常處理不同類型數(shù)據(jù)轉(zhuǎn)換的接口或者類,可以使用泛型定義,如Java中的List接口。定義泛型接口或類的過程,與定義一個(gè)接口或者類相似。
(1)泛型類
泛型類簡(jiǎn)單地說就是具有一個(gè)或者多個(gè)類型參數(shù)的類。
定義泛型類的語法格式如下。
訪問修飾符 class className<TypeList>
TypeList表示類型參數(shù)列表,每個(gè)類型變量之間以逗號(hào)分隔。
例如:
public class GenericClass<T>{……}
創(chuàng)建泛型類實(shí)例的語法格式如下:
new className<TypeList>(argList);
- TypeList表示定義的類型參數(shù)列表,每個(gè)類型變量之間以逗號(hào)分隔。
- argList表示實(shí)際傳遞的類型參數(shù)列表,每個(gè)類型變量之間同樣以逗號(hào)分隔。
例如:
new GenericClass<String>("this is String object")
(2)泛型接口
泛型接口就是擁有一個(gè)或多個(gè)類型參數(shù)的接口。泛型接口的定義方式與定義泛型類類似。
定義泛型接口的語法格式如下。
訪問修飾符interface interfaceName<TypeList>
TypeList表示由逗號(hào)分隔的一個(gè)或多個(gè)類型參數(shù)列表。
例如:
public interface TestInterface<T>{ public T print(T t); }
泛型類實(shí)現(xiàn)泛型接口的語法格式如下。
訪問修飾符 class className<TypeList> implements interfaceName<TypeList>
示例13
定義泛型接口、泛型類,泛型類實(shí)現(xiàn)泛型接口,在泛型類中添加相應(yīng)的泛型方法。
實(shí)現(xiàn)步驟如下。
1)定義泛型接口TestInterface<T>,添加方法getName(),并設(shè)置返回類型為T。
2)定義泛型類Student<T>,并實(shí)現(xiàn)接口TestInterface<T>,聲明類型為T的字段name,添加構(gòu)造方法。
3)使用Student<T>實(shí)例化TestInterface<T>。
關(guān)鍵代碼:
//定義泛型接口 interface TestInterface<T>{ public T getName(); //設(shè)置的類型由外部決定 } //定義泛型類 class Student<T> implements TestInterface<T>{ //實(shí)現(xiàn)接口TestInterface<T> private T name; //設(shè)置的類型由外部決定 public Student(T name){ this.setName(name); } public void setName(T name){ this.name=name; } public T getName(){ //返回類型由外部決定 return this.name; } } public class GenericesClass{ public static void main(String[] args){ TestInterface<String> student=new Student<String>("張三"); //① System.out.println(student.getName()); } }
輸出結(jié)果:
張三
在示例13中,①的代碼用來創(chuàng)建Student對(duì)象,Student泛型類的泛型參數(shù)定義為String類型,執(zhí)行此代碼后,TestInterface接口和Student類中的泛型參數(shù)類型都為String。并會(huì)通過Student類中只有一個(gè)String參數(shù)的構(gòu)造方法來創(chuàng)建對(duì)象,則name屬性的值為“張三”。
(3)泛型方法
一些方法常常需要對(duì)某一類型數(shù)據(jù)進(jìn)行處理,若處理的數(shù)據(jù)類型不確定,則可以通過泛型方法的方式來定義,達(dá)到簡(jiǎn)化代碼、提高代碼重用性的目的。
泛型方法實(shí)際上就是帶有類型參數(shù)的方法。需要特別注意的是,定義泛型方法與方法所在的類、或者接口是否是泛型類或者泛型接口沒有直接的聯(lián)系,也就是說無論是泛型類還是非泛型類,如果需要就可以定義泛型方法。
定義泛型方法的語法格式如下。
訪問修飾符 <類型參數(shù)> 返回值 方法名(類型參數(shù)列表)
例如:
public <String> void showName(String s){ }
注意在泛型方法中,類型變量放置在訪問修飾符與返回值之間。
示例14
定義泛型方法并調(diào)用。
實(shí)現(xiàn)步驟如下。
1)定義泛型方法。
2)調(diào)用泛型方法。
關(guān)鍵代碼:
public class GenericMethod { //定義泛型方法 public <Integer> void showSize(Integer o){ System.out.println(o.getClass().getName()); } public static void main(String[] args) { GenericMethod gm=new GenericMethod(); gm.showSize(10); } }
輸出結(jié)果:
java.lang.Integer
2. 多個(gè)參數(shù)的泛型類
前面的示例中,泛型類的類型參數(shù)都只有一個(gè),實(shí)際上類型參數(shù)可以有多個(gè),如HashMap<K,V>就有兩個(gè)類型參數(shù),一個(gè)指定key的類型,一個(gè)指定value的類型。下面介紹如何自定義一個(gè)包含多個(gè)類型參數(shù)的泛型類。
示例15
定義泛型類,并設(shè)置兩個(gè)類型參數(shù)。
實(shí)現(xiàn)步驟如下。
(1)定義泛型類。
(2)實(shí)例化泛型類。
關(guān)鍵代碼:
//創(chuàng)建泛型類 class GenericDemo<T,V>{ private T a; private V b; public GenericDemo(T a,V b){ this.a=a; this.b=b; } public void showType(){ System.out.println("a的類型是"+a.getClass().getName()); System.out.println("b的類型是"+b.getClass().getName()); } } //實(shí)例化泛型類 public class Demo{ public static void main(String[] args) { GenericDemo<String,Integer> ge1=new GenericDemo<String,Integer>("Jack",23); ge1.showType(); } }
輸出結(jié)果:
a的類型是java.lang.String b的類型是java.lang.Integer
在示例15中,GenericDemo<T,V>類定義了兩個(gè)類型參數(shù),分別是T和V。定義時(shí)這兩個(gè)類型變量的具體類型并不知道。注意,當(dāng)在一個(gè)泛型中,需要聲明多個(gè)類型參數(shù)時(shí),只需要在每個(gè)類型參數(shù)之間使用逗號(hào)將其隔開即可。在實(shí)例化泛型類時(shí),就需要傳遞兩個(gè)類型參數(shù),這里分別使用了String和Integer代替了T和V。
3. 從泛型類派生子類
面向?qū)ο蟮奶匦酝瑯舆m用于泛型類,所以泛型類也可以繼承。不過,繼承了泛型類的子類,必須也是泛型類。
繼承泛型類的語法格式如下。
class 子類<T> extends 父類<T>{ }
示例16
定義泛型父類,同時(shí)定義一個(gè)泛型子類繼承泛型父類。
實(shí)現(xiàn)步驟如下。
(1)定義父類Farm<T>,并添加整型字段plantNum、方法plantCrop(T crop)。
(2)定義子類FruitFarm <T>,重寫方法plantCrop (List<T> list)。
關(guān)鍵代碼:
//父類Farm<T>(農(nóng)場(chǎng)類) public class Farm<T>{ protected int plantNum=0; //農(nóng)作物種植數(shù)量 public void plantCrop(T crop){ //種植農(nóng)作物的方法 plantNum ++; } } //子類果園類繼承泛型類Farm<T> public class FruitFarm<T> extends Farm<T>{ public void plantCrop(List<T> list){ //重寫種植農(nóng)作物的方法 plantNum+=list.size(); } }
至此,任務(wù)2已經(jīng)全部完成。
- Learning Java Functional Programming
- 從零開始:數(shù)字圖像處理的編程基礎(chǔ)與應(yīng)用
- JavaScript全程指南
- HTML5移動(dòng)Web開發(fā)技術(shù)
- The Android Game Developer's Handbook
- Hands-On Machine Learning with scikit:learn and Scientific Python Toolkits
- 垃圾回收的算法與實(shí)現(xiàn)
- Mastering phpMyAdmin 3.4 for Effective MySQL Management
- Hands-On Microservices with Kotlin
- Unity Game Development Scripting
- Learning Unity 2D Game Development by Example
- Quantum Computing and Blockchain in Business
- Getting Started with Nano Server
- 實(shí)戰(zhàn)Java高并發(fā)程序設(shè)計(jì)(第2版)
- Distributed Computing in Java 9