官术网_书友最值得收藏!

任務(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. 泛型的定義

0

泛型集合

將對(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)全部完成。

主站蜘蛛池模板: 连城县| 安仁县| 隆回县| 苗栗县| 饶平县| 东阿县| 新晃| 乐都县| 工布江达县| 雷波县| 高阳县| 泰来县| 滦平县| 东宁县| 五家渠市| 锦屏县| 崇礼县| 合水县| 乐山市| 冷水江市| 乐山市| 澎湖县| 沙坪坝区| 无锡市| 福贡县| 体育| 海安县| 天门市| 湘乡市| 乐安县| 东宁县| 伊金霍洛旗| 九龙县| 宣恩县| 汝城县| 泸西县| 万州区| 徐州市| 项城市| 济宁市| 诏安县|