1.一個簡單的范型示例
在以前,你可能遇到過這樣的代碼:
注意,第三行需要強制轉(zhuǎn)換。而使用范型:
這里將list聲明成String類型的List。List是有一個類型參數(shù)的范型接口。這個例子中類型參數(shù)是String。
?
2.定義簡單的范型
看j2se5.0中List和Iterator接口的實現(xiàn)(片斷):
上面的代碼我們比較熟悉,但是其中增加了尖括號。尖括號中的內(nèi)容定義了接口List和Iterator的形式類型參數(shù)。類型參數(shù)可以用在范型聲明中,如類和接口的聲明。
一旦聲明了范型,你就可以使用它。在上面的例子中使用了List<String>。這里使用String是實參,代替了形參E。如果使用List<Integer>,則用實參Integer代替了形參E。
不管List<Integer>還是List<String>,它們的類只有一個??紤]下面的代碼:
輸出結(jié)果為true。
?
一般來說,形式類型參數(shù)都是大寫,盡量使用單個字母,許多容器類都使用E作為參數(shù)。
?
3.范型和繼承
考慮下面的代碼,你認(rèn)為它會出錯嗎?
當(dāng)然,String類繼承Object類,這樣做不會出錯。但下面的代碼呢?
編譯出錯!
是的,List<Object>和List<String>沒有繼承關(guān)系。
?
4.通配符
考慮下面一個方法:
事實上,上面這個方法并不通用,它只能打印Collection<Object>類型的集合,象其他的如Collection<String>、Collection<Integer>并不能被打印,因為對象類型不一致。
為了解決這個問題,可以使用通配符:
Collection<?>被稱作未知類型的集合。問號代表各種類型。
上面的讀取集合中的數(shù)據(jù)時,我們采用Object類型。這樣做時可以的,因為不管未知類型最終代表何種類型,它的數(shù)據(jù)都繼承Object類,那么再考慮一下下面的代碼:
這樣做時錯誤的,因為我們不知道?代表何種類型,所以我們不能直接將Object增加到集合中,這會出現(xiàn)類型不匹配的情況。
?
5.有限制的通配符
考慮下面的代碼
考慮下面的范型方法:
這個范型方法只能顯示List<Man>類型的數(shù)據(jù),下面的代碼允許顯示Man和它的子類。
這里使用??extends?Man代替Man,表明接受任何Man的子類做為參數(shù)。
和前面的代碼類似,下面的代碼也是不正確的:
原因也很簡單,因為?代表一切繼承Man的類,你并不能保證就一定時GoodMan類。
?
和這種用法類似:
6.范型方法
考慮下面的代碼,我們將一個數(shù)組的內(nèi)容加到一個集合中
這段代碼時錯的!
因為我們并不知道集合C的類型,所以不能將Man類型的數(shù)據(jù)加到集合中。
可以使用范型方法解決:
這里T時一個形式類型參數(shù)。
何時該采用通用方法?何時該采用通配符?
考慮下面的例子:
改寫成通用方法
然而,在這里每個方法T只使用了一次,返回值不依賴形式參數(shù),其他參數(shù)也不依賴形式參數(shù)。這說明實參被用作多態(tài),這種情況下就應(yīng)該用通配符。
在以前,你可能遇到過這樣的代碼:
1
List?list?=?new?LinkedList();?
2
3
list.add("a");?
4
5
list.add("b");?
6
7
list.add("c");?
8
9
String?name?=?(String)list.iterator.next();?

2

3

4

5

6

7

8

9

注意,第三行需要強制轉(zhuǎn)換。而使用范型:
?1
?2
List<String>?list?=?new?LinkedList<String>();?
?3
?4
list.add("a")?
?5
?6
list.add("b");?
?7
?8
list.add("c");?
?9
10
String?name?=?list.iterator.next();?
11
12

?2

?3

?4

?5

?6

?7

?8

?9

10

11

12

這里將list聲明成String類型的List。List是有一個類型參數(shù)的范型接口。這個例子中類型參數(shù)是String。
?
2.定義簡單的范型
看j2se5.0中List和Iterator接口的實現(xiàn)(片斷):
?1
?2
public?interface?List<E>??
?3
?4

{??
?5
?6
void?add(E?x);?
?7
?8
Iterator<E>?iterator();?
?9
10
}?
11
12
public?interface?Iterator<E>??
13
14

{??
15
16
E?next();?
17
18
boolean?hasNext();?
19
20
}??
21
22

?2

?3

?4



?5

?6

?7

?8

?9

10

11

12

13

14



15

16

17

18

19

20

21

22

上面的代碼我們比較熟悉,但是其中增加了尖括號。尖括號中的內(nèi)容定義了接口List和Iterator的形式類型參數(shù)。類型參數(shù)可以用在范型聲明中,如類和接口的聲明。
一旦聲明了范型,你就可以使用它。在上面的例子中使用了List<String>。這里使用String是實參,代替了形參E。如果使用List<Integer>,則用實參Integer代替了形參E。
不管List<Integer>還是List<String>,它們的類只有一個??紤]下面的代碼:
1
2
List<String>?list1?=?new?LinkedList<String>();?
3
4
List<Integer>?list2?=?new?LinkedList<Integer>();?
5
6
System.out.println(list1.getClass()==list2.getClass());?
7
?
2

3

4

5

6

7

輸出結(jié)果為true。
?
一般來說,形式類型參數(shù)都是大寫,盡量使用單個字母,許多容器類都使用E作為參數(shù)。
?
3.范型和繼承
考慮下面的代碼,你認(rèn)為它會出錯嗎?
1
2
String?s?="?xxx@gmail.com";?
3
4
Object?o?=?s:?
5

2

3

4

5

當(dāng)然,String類繼承Object類,這樣做不會出錯。但下面的代碼呢?
1
List<String>?s?=?new?LinkedList<String>();?
2
3
List<Object>o=s;?
4

2

3

4

編譯出錯!
是的,List<Object>和List<String>沒有繼承關(guān)系。
?
4.通配符
考慮下面一個方法:
?1
?2
public?void?printCollection(Collection<Object>?c)?
?3
?4

{?
?5
?6
???for(Object?o:c)?
?7
?8

{?
?9
10
???System.out.printf("%s%n",o);?
11
12
}?
13
14
}?
15

?2

?3

?4



?5

?6

?7

?8



?9

10

11

12

13

14

15

事實上,上面這個方法并不通用,它只能打印Collection<Object>類型的集合,象其他的如Collection<String>、Collection<Integer>并不能被打印,因為對象類型不一致。
為了解決這個問題,可以使用通配符:
?1
?2
public?void?printCollection(Collection<?>?c)?
?3
?4

{?
?5
?6
???for(Object?o:c)?
?7
?8

{?
?9
10
???System.out.printf("%s%n",o);?
11
12
}?
13
14
}?
15
16

?2

?3

?4



?5

?6

?7

?8



?9

10

11

12

13

14

15

16

Collection<?>被稱作未知類型的集合。問號代表各種類型。
上面的讀取集合中的數(shù)據(jù)時,我們采用Object類型。這樣做時可以的,因為不管未知類型最終代表何種類型,它的數(shù)據(jù)都繼承Object類,那么再考慮一下下面的代碼:
1
2
Collection<?>?c?=?new?ArrayList<String>();?
3
4
c.add(new?Object());???
5
6

2

3

4

5

6

這樣做時錯誤的,因為我們不知道?代表何種類型,所以我們不能直接將Object增加到集合中,這會出現(xiàn)類型不匹配的情況。
?
5.有限制的通配符
考慮下面的代碼
?1
?2
class?Man?
?3
?4

{?
?5
?6
???public?String?name?="";?
?7
?8
}?
?9
10
class?GoodMan?extends?Man?
11
12

{?
13
14
???public?String?name?=?"";?
15
16
}?
17
18
class?BadMan?extends?Man?
19
20

{?
21
22
???public?String?name?=?"";?
23
24
}?
25
26

?2

?3

?4



?5

?6

?7

?8

?9

10

11

12



13

14

15

16

17

18

19

20



21

22

23

24

25

26

考慮下面的范型方法:
?1
?2
public?void?printName(List<Man>?men)?
?3
?4

{?
?5
?6
???for(Man?man:men)?
?7
?8
???
{?
?9
10
??????System.out.println("姓名:"+?man.name);?
11
12
???}?
13
14
}?
15
16

?2

?3

?4



?5

?6

?7

?8



?9

10

11

12

13

14

15

16

這個范型方法只能顯示List<Man>類型的數(shù)據(jù),下面的代碼允許顯示Man和它的子類。
?1
?2
public?void?printName(List<??extends??Man>?men)?
?3
?4

{?
?5
?6
???for(Man?man:men)?
?7
?8
???
{?
?9
10
??????System.out.println("姓名:"?+?man.name);?
11
12
???}?
13
14
}?
15
16

?2

?3

?4



?5

?6

?7

?8



?9

10

11

12

13

14

15

16

這里使用??extends?Man代替Man,表明接受任何Man的子類做為參數(shù)。
和前面的代碼類似,下面的代碼也是不正確的:
?1
?2
public?void?adman(List<??extends?Man>?men)?
?3
?4

{?
?5
?6
???GoodMan?good?=?new?GoodMan();?
?7
?8
???good.name?=?"zhupan";?
?9
10
???men.add(good);?
11
12
}?
13
14

?2

?3

?4



?5

?6

?7

?8

?9

10

11

12

13

14

原因也很簡單,因為?代表一切繼承Man的類,你并不能保證就一定時GoodMan類。
?
和這種用法類似:
?1
?2
public?void?adman(List<??super?GoodMan>?men)?
?3
?4

{?
?5
?6
???GoodMan?good?=?new?GoodMan();?
?7
?8
???good.name?=?"zhupan";?
?9
10
???men.add(good);?
11
12
}?
13

?2

?3

?4



?5

?6

?7

?8

?9

10

11

12

13

6.范型方法
考慮下面的代碼,我們將一個數(shù)組的內(nèi)容加到一個集合中
?1
?2
public??void?copyArrayToCollection(Man[]?men,?Collection<?>c)?
?3
?4

{?
?5
?6
for(Man?man:men)?
?7
?8

{?
?9
10
???c.add(man);?
11
12
}?
13
14
}?
15
16

?2

?3

?4



?5

?6

?7

?8



?9

10

11

12

13

14

15

16

這段代碼時錯的!
因為我們并不知道集合C的類型,所以不能將Man類型的數(shù)據(jù)加到集合中。
可以使用范型方法解決:
?1
?2
public?<T>?void?copyArrayToCollection(T[]?men,?Collection<T>c)?
?3
?4

{?
?5
?6
for(T?man:men)?
?7
?8

{?
?9
10
???c.add(man);?
11
12
}?
13
14
}?
15

?2

?3

?4



?5

?6

?7

?8



?9

10

11

12

13

14

15

這里T時一個形式類型參數(shù)。
何時該采用通用方法?何時該采用通配符?
考慮下面的例子:
?1
?2
interface??Collection<E>?
?3
?4

{?
?5
?6
??public?boolean?containsAll(Collection<?>?c);?
?7
?8
public?boolean?addAll(Collection<??extends?E>?c);?
?9
10
}?
11
12

?2

?3

?4



?5

?6

?7

?8

?9

10

11

12

改寫成通用方法
?1
?2
interface??Collection<E>?
?3
?4

{?
?5
?6
???public?<T>?boolean?containsAll(Collection<T>?c);?
?7
?8
public?<T?extends?E>?boolean?addAll(Collection<T>?c);?
?9
10
}?
11
12

?2

?3

?4



?5

?6

?7

?8

?9

10

11

12

然而,在這里每個方法T只使用了一次,返回值不依賴形式參數(shù),其他參數(shù)也不依賴形式參數(shù)。這說明實參被用作多態(tài),這種情況下就應(yīng)該用通配符。