1.一个简单的范型示例
在以前,你可能遇到过这样的代码:
注意,第三行需要强制转换。而使用范型:
这里将list声明成String类型的List。List是有一个类型参数的范型接口。这个例子中类型参数是String。
2.定义简单的范型
看j2se5.0中List和Iterator接口的实现(片断):
上面的代码我们比较熟悉,但是其中增加了尖括号。尖括号中的内容定义了接口List和Iterator的形式类型参数。类型参数可以用在范型声明中,如类和接口的声明。
一旦声明了范型,你就可以使用它。在上面的例子中使用了List<String>。这里使用String是实参,代替了形参E。如果使用List<Integer>,则用实参Integer代替了形参E。
不管List<Integer>还是List<String>,它们的类只有一个。考虑下面的代码:
输出结果为true。
一般来说,形式类型参数都是大写,尽量使用单个字母,许多容器类都使用E作为参数。
3.范型和继承
考虑下面的代码,你认为它会出错吗?
当然,String类继承Object类,这样做不会出错。但下面的代码呢?
编译出错!
是的,List<Object>和List<String>没有继承关系。
4.通配符
考虑下面一个方法:
事实上,上面这个方法并不通用,它只能打印Collection<Object>类型的集合,象其他的如Collection<String>、Collection<Integer>并不能被打印,因为对象类型不一致。
为了解决这个问题,可以使用通配符:
Collection<?>被称作未知类型的集合。问号代表各种类型。
上面的读取集合中的数据时,我们采用Object类型。这样做时可以的,因为不管未知类型最终代表何种类型,它的数据都继承Object类,那么再考虑一下下面的代码:
这样做时错误的,因为我们不知道?代表何种类型,所以我们不能直接将Object增加到集合中,这会出现类型不匹配的情况。
5.有限制的通配符
考虑下面的代码
考虑下面的范型方法:
这个范型方法只能显示List<Man>类型的数据,下面的代码允许显示Man和它的子类。
这里使用? extends Man代替Man,表明接受任何Man的子类做为参数。
和前面的代码类似,下面的代码也是不正确的:
原因也很简单,因为?代表一切继承Man的类,你并不能保证就一定时GoodMan类。
和这种用法类似:
6.范型方法
考虑下面的代码,我们将一个数组的内容加到一个集合中
这段代码时错的!
因为我们并不知道集合C的类型,所以不能将Man类型的数据加到集合中。
可以使用范型方法解决:
这里T时一个形式类型参数。
何时该采用通用方法?何时该采用通配符?
考虑下面的例子:
改写成通用方法
然而,在这里每个方法T只使用了一次,返回值不依赖形式参数,其他参数也不依赖形式参数。这说明实参被用作多态,这种情况下就应该用通配符。
在以前,你可能遇到过这样的代码:
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();
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();
注意,第三行需要强制转换。而使用范型:
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
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
这里将list声明成String类型的List。List是有一个类型参数的范型接口。这个例子中类型参数是String。
2.定义简单的范型
看j2se5.0中List和Iterator接口的实现(片断):
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
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
上面的代码我们比较熟悉,但是其中增加了尖括号。尖括号中的内容定义了接口List和Iterator的形式类型参数。类型参数可以用在范型声明中,如类和接口的声明。
一旦声明了范型,你就可以使用它。在上面的例子中使用了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
List
<
String
>
list1
=
new
LinkedList
<
String
>
(); 3

4
List
<
Integer
>
list2
=
new
LinkedList
<
Integer
>
(); 5

6
System.out.println(list1.getClass()
==
list2.getClass()); 7
输出结果为true。
一般来说,形式类型参数都是大写,尽量使用单个字母,许多容器类都使用E作为参数。
3.范型和继承
考虑下面的代码,你认为它会出错吗?
1
2
String s =
"
ctguzhupan@163.com
"
;
3
4
Object o
=
s:
5
2
String s =
"
ctguzhupan@163.com
"
; 3

4
Object o
=
s: 5
当然,String类继承Object类,这样做不会出错。但下面的代码呢?
1
List
<
String
>
s
=
new
LinkedList
<
String
>
();
2
3
List
<
Object
>
o
=
s;
4
List
<
String
>
s
=
new
LinkedList
<
String
>
(); 2

3
List
<
Object
>
o
=
s; 4
编译出错!
是的,List<Object>和List<String>没有继承关系。
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
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
事实上,上面这个方法并不通用,它只能打印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
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
Collection<?>被称作未知类型的集合。问号代表各种类型。
上面的读取集合中的数据时,我们采用Object类型。这样做时可以的,因为不管未知类型最终代表何种类型,它的数据都继承Object类,那么再考虑一下下面的代码:
1
2
Collection
<?>
c
=
new
ArrayList
<
String
>
();
3
4
c.add(
new
Object());
5
6
2
Collection
<?>
c
=
new
ArrayList
<
String
>
(); 3

4
c.add(
new
Object()); 5

6
这样做时错误的,因为我们不知道?代表何种类型,所以我们不能直接将Object增加到集合中,这会出现类型不匹配的情况。
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
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
考虑下面的范型方法:
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
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
这个范型方法只能显示List<Man>类型的数据,下面的代码允许显示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
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
这里使用? extends Man代替Man,表明接受任何Man的子类做为参数。
和前面的代码类似,下面的代码也是不正确的:
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
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
原因也很简单,因为?代表一切继承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
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
6.范型方法
考虑下面的代码,我们将一个数组的内容加到一个集合中
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
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
这段代码时错的!
因为我们并不知道集合C的类型,所以不能将Man类型的数据加到集合中。
可以使用范型方法解决:
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
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
这里T时一个形式类型参数。
何时该采用通用方法?何时该采用通配符?
考虑下面的例子:
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
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
改写成通用方法
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
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
然而,在这里每个方法T只使用了一次,返回值不依赖形式参数,其他参数也不依赖形式参数。这说明实参被用作多态,这种情况下就应该用通配符。
本文介绍了Java范型的基础概念,包括如何定义和使用泛型类及方法,并解释了通配符和受限通配符的作用。
1万+

被折叠的 条评论
为什么被折叠?



