Java : 泛型的概念与用法
一、什么是泛型
在之前开发Android应用的时候,经常需要用到Map集合往ListView中添加图片和文字。所以经常用到下面的语句:
[code lang=”Java”]
//往List集合中添加Map集合,Map集合中添加icon 和 item
List> list = new ArrayList>();
for (int i = 0; i < icon.length; i++) {
Map map = new HashMap();
map.put("icon", icon[i]);
map.put("item", item[i]);
list.add(map);
}
[/code]
由于Java基础学了一半转战Android,不是很明白泛型的概念和运用,所以下定决心弄明白。
泛型可能是面向对象程序设计语言中很常用的一个概念。很多人知道如何运用,但是讲不清楚到底是什么。
那么什么是泛型呢?先来看下面这段代码:
[code lang=”Java”]
//创建一个List集合
ArrayList al = new ArrayList();
al.add(123); //添加基本数据类型对象
al.add("123"); //添加String对象
al.add(new Person("123")); //添加自定义Person对象
[/code]
这个代码很简单,前面的集合部分已经说过了,集合可以存任意对象,也就是Object。那么上面的集合添加了三种对象都是不同类型的。
那么在程序开发中很容易设想到,如果只想在这个集合中添加String类型该怎么做呢?
尝试把代码改成如下格式:
[code lang=”Java”]
//创建一个List集合 并指定集合的对象类型
ArrayList al = new ArrayList();
al.add("123");
al.add("iamxiarui");
[/code]
可以看到,当指定了集合中的对象类型的后,集合只能存入相关类型。如果存入的类型跟指定类型不匹配,编译器会直接报错。所以给出泛型定义:
泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
泛型的格式:通过 < > 来定义要操作的引用数据类型。而且必须是引用数据类型,不能是基本数据类型,比如int 必须 改成 Integer。
现在再回头看文章开头那段Android应用中的代码:
[code lang=”Java”]
//List集合中 指定元素类型是Map集合 ,Map集合中指定的key是String类型,value是Object类型
List> list = new ArrayList>();
for (int i = 0; i &lt; icon.length; i++) {
Map map = new HashMap();
//Map中添加的key是String , value是数组中的元素,是Object类型
map.put("icon", icon[i]);
map.put("item", item[i]);
list.add(map);
}
[/code]
二、泛型的作用
既然泛型是JavaSE 1.5之后给出的新特性,那么它肯定弥补了之前版本的不足,也肯定带了一些额外的作用。
1、泛型是一个类型安全机制,能够解决代码中的安全问题。
安全问题一直是面向对象程序设计语言的一大重要问题。很多此类问题都不是在运行或者Debug的时候能检查出来的。而一旦将代码运用于实例项目中,很可能出现各种问题从而降低项目的安全性。
而最有效也是最简洁的方法就是在代码编写过程中,就遵从某种书写规范,从根本上解决这类问题。泛型也是这类规范中的一种。
2、可以将运行时期出现的异常问题转移到编译时期,让运行时期的问题减少。
这个问题也是很常见的,比如我们在原始代码中并没有规定集合中存入元素的类型,那么在每次更新代码的时候都需要注意应该存入什么样的类型。假如在书写过程中出现错误,编译器不能及时报错。只有等运行程序的时候才能发现错误。这样做增加了开发成本,降低了效率。但是一旦用泛型规定了元素的类型,在代码书写过程中,就能及时发现错误,提高了开发效率。
3、避免了强制转换的繁琐问题。
这个很容易理解。一般在程序开发过程中,类型转换肯定是一个经常遇到的问题。在没有规定元素类型的情况下,在调用集合元素的时候,一般都需要强制转换类型。假如集合中的类型过多的话,代码就变得冗余繁杂。但是规定元素类型后,则不需要或者很少使用类型的强制转换。进一步提高效率。
三、泛型限定
所谓泛型限定基本上已经属于泛型的进阶内容了。在阅读API文档中,我们经常能看见一些 T、E、K、?等字符。那么这些字符作用是什么呢?
1、不明确具体泛型类型的时候,用<?>来表示:?是通配符,也就是占位符。
[code lang=”Java”]
//集合迭代器方法
public static void print(ArrayList> al){
Iterator> it = al.iterator();
}
[/code]
从上面的代码可以看到,这是一个方法。方法的调用可能适用于很多个程序。而且在调用的时候并不知道到底传什么样类型的集合进去,这个时候将类型定义成 ? 就显得非常简洁方便了。不需要为每一种类型集合都定义相应的方法,只需要定义一个通配类型的集合方法就行了。
2、泛型上限 : ? extends E 这个意思表示当前集合类型可以是E类型或者E的子类型。
[code lang=”Java”]
//表示可以该集合存入Person类 或者 Person 类的子类型
public static void print(ArrayList extends Person> al){
Iterator< extends Person> it = al.iterator();
}
[/code]
3、泛型下限:? super E 这个意思表示当前集合类型可以是E类型或者E的父类型。
[code lang=”Java”]
//表示可以该集合存入Student类 或者 Student 类的父类型
public static void print(ArrayList super Student> al){
Iterator super Student> it = al.iterator();
}
[/code]
四、自定义泛型
在泛型的定义中有这样一句话:这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
1、泛型类:当类中的引用数据类型不确定的时候,可以定义泛型类。
[code lang=”Java”]
// 自定义泛型:定义了Generic类的类型是Person对象
class Generic {
private Person p;
public Person getP() {
System.out.println(p);
return p;
}
public void setP(Person p) {
this.p = p;
}
[/code]
2、泛型方法:在类中的方法上定义出泛型。
[code lang=”Java”]
// 自定义泛型:定义了Generic类的类型是Person对象
class Demo{
public void show(T t){
System.out.println(t);
}
publicvoid show(Q q){
System.out.println(q);
}
}
[/code]
特别的:当静态方法不可以访问类上定义的泛型,如果静态方法操作的引用数据类型不能确定,可以将泛型定义在静态方法上。
[code lang=”Java”]
// 类上已经定义了泛型
class GenericMethod {
// 方法上没有定义泛型 则根据类确定泛型
public void getB(B b) {
System.out.println("B — " + b);
}
// 方法上已经定义了泛型 根据方法泛型来
public void getC(C c) {
System.out.println("C — " + c);
}
// 静态方法 必须自定义方法泛型 不能根据类上的泛型
public static void getD(D d) {
System.out.println("D — " + d);
}
}
[/code]
3、泛型接口:定义接口的泛型方法。
[code lang=”Java”]
// 定义接口的泛型
interface Intet {
void show(T t);
}
// 类实现接口 并自定义泛型
class GenericInterface implements Intet {
public void show(T t) {
System.out.println("show — " + t);
}
}
[/code]
项目源码: