泛型实质上就是使程序员定义安全的类型。泛型的出现不仅可以让程序员少写些代码,主要的作用是解决类型安全问题,它提供编译时的安全检查,不会因为将对象置于某个容器中而失去其类型。在没有出现泛型之前,Java也提供了对Object的引用“任意化”操作,这种“任意化”操作就是对Object引用进行向下转型和向上转型操作,但某些强制类型转换的错误也许不会被编译器捕获,而在运行后出现异常,可见强制类型转换存在安全隐患,所以在此Java提供了泛型机制。
1、泛型的定义
Object类为最上层的父类,很多程序员为了使程序更为通用,设计程序时通常使传入的值与返回的值都以Object类型为主。当需要使用这些实例时,必须正确地将该实例转换为原来的类型,否则在运行时将会发生ClassCastException异常。
在JDK 1.5版本以后,提出了泛型机制。其语法如下:
类名<T>
其中,T代表一个类型的名称。
示例:定义一个泛型类。
/**
* 定义泛型类
*
* @author pan_junbiao
*
* @param <T>
*/
public class OverClass<T>
{
// 定义泛型成员变量
private T over;
public T getOvre()
{
return over;
}
public void setOver(T over)
{
this.over = over;
}
public static void main(String[] args)
{
// 实例化一个Boolean型的对象
OverClass<Boolean> over1 = new OverClass<Boolean>();
// 实例化一个Float型的对象
OverClass<Float> over2 = new OverClass<Float>();
over1.setOver(true);
over2.setOver(12.3f);
Boolean b = over1.getOvre();
Float f = over2.getOvre();
System.out.println(b);
System.out.println(f);
}
}
执行结果:
说明:在定义泛型类时,一般类型名称使用T来表达,而容器的元素使用E来表达,具体的设置读者可以参看JDK 5.0以上版本的API。
2、泛型的常规用法
2.1 定义泛型类时声明多个类型
在定义泛型时,可以声明多个类型。语法如下:
public class MutiOverClass<T1, T2>{
}
其中,T1和T2为可能被定义的类型。
这样在实例化指定类型的对象时就可以指定多个类型。例如:
MutiOverClass<Boolean, Float> muti = new MutiOverClass<Boolean, Float>();
2.2 定义泛型类时声明数组类型
定义泛型类时也可以声明数组类型。
示例:定义泛型类声明数组类型。
/**
* 定义泛型类声明数组类型
*
* @author pan_junbiao
*
* @param <T>
*/
public class ArrayClass<T>
{
private T[] array; // 定义泛型数组
public void SetT(T[] array)
{ // 设置SetXXX()方法为成员数组赋值
this.array = array;
}
public T[] getT()
{ // 获取成员数组
return array;
}
public static void main(String[] args)
{
ArrayClass<String> a = new ArrayClass<String>();
String[] array = { "KevinPan", "pan_junbiao", "pan_junbiao的博客" };
a.SetT(array); // 调用SetT()方法
for (int i = 0; i < a.getT().length; i++)
{
System.out.println(a.getT()[i]); // 调用getT()方法返回数组中的值
}
}
}
执行结果:
可见,可以在使用泛型机制时声明一个数组,但是不可以使用泛型来创建数组实例。例如,下面的代码就是错误的:
public class ArrayClass<T>
{
private T[] array = new T[10]; // 报错:不能使用泛型来创建数组的实例
}
2.3 集合类声明容器的元素
可以使用K和V两个字符代表容器中的键值和与键值对应的具体值。
示例:使用集合类声明容器的元素。
import java.util.HashMap;
import java.util.Map;
/**
* 使用集合类声明容器的元素
*
* @author pan_junbiao
*
* @param <K>
* @param <V>
*/
public class MutiOverClass<K, V>
{
public Map<K, V> m = new HashMap<K, V>(); // 定义一个集合HashMap实例
// 设置put()方法,将对应的键值与键名存入集合对象中
public void put(K k, V v)
{
m.put(k, v);
}
public V get(K k)
{ // 根据键名获取键值
return m.get(k);
}
public static void main(String[] args)
{
// 实例化泛型类对象
MutiOverClass<Integer, String> mu = new MutiOverClass<Integer, String>();
for (int i = 0; i < 5; i++)
{
// 根据集合的长度循环将键名与具体值放入集合中
mu.put(i, "pan_junbiao的博客_" + i);
}
for (int i = 0; i < mu.m.size(); i++)
{
// 调用get()方法获取集合中的值
System.out.println(mu.get(i));
}
}
}
执行结果:
其实上述示例纯属多余,因为在Java中这些集合框架已经都被泛型化了。
常用的被泛型化的集合类:
集合类 | 泛型定义 |
---|---|
ArrayList | ArrayList<E> |
HashMap | HashMap<K,V> |
HashSet | HashSet<E> |
Vector | Vector<E> |
示例:使用泛型实例化常用集合类。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;
/**
* 使用泛型实例化常用集合类
*
* @author pan_junbiao
*
*/
public class AnyClass
{
public static void main(String[] args)
{
// 定义ArrayList容器,设置容器内的值类型为Integer
ArrayList<Integer> a = new ArrayList<Integer>();
a.add(1); // 为容器添加新值
for (int i = 0; i < a.size(); i++)
{
// 根据容器的长度循环显示容器内的值
System.out.println("获取ArrayList容器的值:" + a.get(i));
}
// 定义HashMap容器,设置容器的键名与键值类型分别为Integer与String型
Map<Integer, String> m = new HashMap<Integer, String>();
for (int i = 0; i < 5; i++)
{
m.put(i, "成员" + i); // 为容器填充键名与键值
}
for (int i = 0; i < m.size(); i++)
{
// 根据键名获取键值
System.out.println("获取Map容器的值" + m.get(i));
}
// 定义Vector容器,使容器中的内容为String型
Vector<String> v = new Vector<String>();
for (int i = 0; i < 5; i++)
{
v.addElement("成员" + i); // 为Vector容器添加内容
}
for (int i = 0; i < v.size(); i++)
{
// 显示容器中的内容
System.out.println("获取Vector容器的值" + v.get(i));
}
}
}
执行结果:
3、泛型的高级用法
泛型的高级用法包括限制泛型可用类型和使用类型通配符等。
3.1 限制泛型可用类型
默认可以使用任何类型来实例化一个泛型类对象,但Java中也对泛型类实例的类型作了限制。语法如下:
class 类名称<T extends anyClass>
其中,anyClass指某个接口或类。
使用泛型限制后,泛型类的类型必须实现或继承了anyClass这个接口或类。无论anyClass是接口还是类,在进行泛型限制是都必须使用extends关键字。
示例:在项目中创建LimitClass类,在该类中限制泛型类型。
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* 限制泛型的类型
*
* @author pan_junbiao
*
* @param <T>
*/
public class LimitClass<T extends List>
{
public static void main(String[] args)
{
// 可以实例化已经实现List接口的类
LimitClass<ArrayList> l1 = new LimitClass<ArrayList>();
LimitClass<LinkedList> l2 = new LimitClass<LinkedList>();
// 错误:因为HashMap没有实现List()接口
// LimitClass<HashMap> l3 = new LimitClass<HashMap>();
}
}
3.2 使用类型通配符
在泛型机制中,提供了类型通配符,其主要作用是在创建一个泛型类型对象时限制这个泛型类的类型实现或继承某个接口或类的子类。要声明这样一个对象可以使用“?”通配符来表示,同时使用extends关键字来对泛型加以限制。
使用泛型类型通配符的语法如下:
泛型类名称<? extends List> a = null;
其中,<? extends List>表示类型未知,当需要使用该泛型对象时,可以单独实例化。
如果使用A<?>这种形式实例化泛型类对象,则默认表示可以将A指定为实例化Object及以下的子类类型。
示例:在泛型中使用通配符形式。
List<String> l1 = new ArrayList<String>(); // 实例化一个ArrayList对象
l1.add("成员"); // 在集合中添加内容
List<?> l2 = l1; // 使用通配符
List<?> l3 = new LinkedList<Integer>();
System.out.println(l2.get(0));
泛型类型限制除了可以向下限制之外,还可以进行向上限制,再要在定义时使用super关键字即可。例如:A<? super List> a = null; 这样定义后,对象a只接受List接口或上层父类类型。
A<? extends E>:表示接受E类型或E的子类型。
A<? super E> :表示接受E类型或E的父类型。
3.3 继承泛型类与实现泛型接口
定义为泛型的类和接口也可以被继承与实现。
示例:在项目中创建一个类文件,在该类中继承泛型类。
public class ExtendClass<T1>
{
}
class SubClass<T1,T2,T3> extends ExtendClass<T1>
{
}
如果在SubClass类继承ExtendClass类时保留父类的泛型类型,需要在继承时指明,如果没有指明,直接使用extends ExtendClass语句进行继承操作,则SubClass类中的T1、T2和T3都会自动变为Object,所以在一般情况下都将父类的泛型类型保留。
定义的泛型接口也可以被实现。
示例:在项目中创建一个类文件,在该类中实现泛型接口。
interface i<T1>
{
}
class SubClass<T1,T2,T3> implements i<T1>
{
}
4、泛型总结
(1)泛型的类型参数只能是类类型,不可以是简单类型,如A<int>这种泛型定义就是错误的。
(2)泛型的类型个数可以是多个。
(3)可以使用extends关键字限制泛型的类型。
(4)可以使用通配符限制泛型的类型。