java5之后提供泛型(generics),使用泛型可以最大限度地重用代码、保护类型的安全、提高性能。
泛型特性对java影响最大的是集合框架的使用。
// 先看一个使用集合的实例
List list = new ArrayList();
// 向集合中添加元素
list.add("1");
list.add("2");
list.add("3");
// 遍历集合
for (Object item : list) {
Integer element = (Integer) item; // 强制类型转换
System.out.println("读取集合元素:" + element);
}
上述,将数据保存到集合中再取出。但对java5之前:放入一种特定类型,但是取出时全部是Object类型,在具体使用时需要将元素转换为特定类型。
强制类型转换是有风险的,如果不进行判断就臆断进行类型转换,会发生ClassCastException异常。
上述有效代码的最后一行,试图将java.lang.String对象转换为java.lang.Integer对象。
在java5之前没有好的解决办法,在类型转换之前要通过instanceof运算符判断该对象是否是目标类型。
而泛型的引入可以将这些运行时异常提前到编译期暴露出来,这增强了类型安全检查。
// 修改代码
List<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");
// list.add(new Date); // 发生编译错误
for (String item : list) {
// Integer element = (Integer) item;
System.out.println("读取集合元素:" + item);
}
这就是List和ArrayList的泛型表示方式,尖括号中可以是任何引用类型,它限定了集合中是否能存放该种类型的对象,所以试图添加非String类型元素时,会发生编译错误。
增强for循环遍历,从集合中取出的元素就是String类型,再试图转换为Integer就会发生编译错误。
(原本在运行时发生的异常,提早暴露到编译期,使程序员及早发现问题,避免程序发布上线之后发生系统崩溃的情况)
在集合中如果没有使用泛型,Eclipse等IDE工具都会警告。可以单击警告,根据Eclipse的修订功能,修订这些警告。在弹出的对话框中选择“推断通用类型参数”就可以添加泛型。
java5后所有集合类型都可以有泛型类型。
// Set泛型集合示例
Set<String> set = new HashSet<String>();
set.add("A");
set.add("D");
set.add("E");
for (String item : set) {
System.out.println(item);
}
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String item = it.next();
System.out.println(item);
}
// Map泛型集合示例
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(102, "张三");
map.put(105, "李四");
Set<Integer> keys = map.keySet();
for (Integer key : keys) {
String value = map.get(key);
System.out.println("key=%d - value=%s \n", key, value);
}
Collection<String> values = map.values();
Itetator<String> it = values.itetator();
while (it.hasNext()) {
String item = it.next();
System.out.println(item);
}
根据自己的需要可以自定义泛型类、泛型接口、带有泛型参数的方法。
数据结构 - 队列(queue),先入先出(FIFO)规则。
java SE提供支持泛型的队列java.util.Queue
自己实现的支持泛型的队列集合:
import java.util.ArrayList;
import java.util.List;
/**
* 自定义的泛型队列集合
*/
// 定义Queue泛型类型的队列,T是参数类型占位符
public class Queue<T> {
// 声明List泛型集合成员变量items,用来保存队列中的元素
private List<T> items;
// 构造方法,初始化集合items
public Queue() {
this.items = new ArrayList<T>();
}
/**
* 入队方法
* @param item 参数需要入队的元素
*/
public void queue(T item) { // 这里的占位符与Queue中的占位符保持一致
this.items.add(item);
}
/**
* 出队方法
* @return 返回出队元素
*/
public T dequeue() { // 这里的占位符与Queue中的占位符保持一致
if (items.isEmpty()) { // 集合是否有元素
return null;
} else {
return this.items.remove(0); // 删除队列的第一个元素,并将删除的元素返回
}
}
@Override
public String toString() {
return items.toString();
}
}
泛型中参数类型占位符可以使任何大写或小写的英文字母,一般情况下习惯使用字母T、E、K、U等大写英文字母。
调用队列示例:
Queue<String> genericQueue = new Queue<String>();
genericQueue.queue("A");
genericQueue.queue("C");
genericQueue.queue("B");
// genericQueue.queue(1); // 编译错误
System.out.println(genericQueue); // [A, C, B]
genericQueue.dequeue();
System.out.println(genericQueue); // [C, B]
Map
与自定义泛型类类似。
将上面示例修改为队列接口:
public interface IQueue<T> {
/**
* 入队方法
* @param item 参数需要入队的元素
*/
public void queue(T item);
/**
* 出队方法
* @return 返回出队元素
*/
public T dequeue();
}
实现接口IQueue
import java.util.ArrayList;
import java.util.List;
/**
* 自定义的泛型队列集合
*/
// 定义Queue泛型类型的队列,T是参数类型占位符
public class ListQueue<T> implements IQueue<T> {
// 声明List泛型集合成员变量items,用来保存队列中的元素
private List<T> items;
// 构造方法,初始化集合items
public ListQueue() {
this.items = new ArrayList<T>();
}
/**
* 入队方法
* @param item 参数需要入队的元素
*/
@Override
public void queue(T item) { // 这里的占位符与Queue中的占位符保持一致
this.items.add(item);
}
/**
* 出队方法
* @return 返回出队元素
*/
@Override
public T dequeue() { // 这里的占位符与Queue中的占位符保持一致
if (items.isEmpty()) { // 集合是否有元素
return null;
} else {
return this.items.remove(0); // 删除队列的第一个元素,并将删除的元素返回
}
}
@Override
public String toString() {
return items.toString();
}
}
实现泛型接口的具体类也应该支持泛型,所以Queue
方法的参数类型或返回值类型可以用类型参数表示。
public static void main(String[] args) {
isEquals(new Integer(1), new Integer(5));
isEquals(1, 5); // 发生了自动装箱
isEquals(new Double(1.0), new Double(1.0));
isEquals("A", "A");
}
public static <T> boolean isEquals(T a, T b) {
return a.equals(b);
}
泛型的类型参数可以设定边界。
比较方法只想用于数值对象大小的比较:
// 限定类型参数为Number
public static <T extends Number> boolean isEquals(T a, T b) {
return a.equals(b);
}