一、泛型简介
Java 5之后提供泛型(Generics)支持,使用泛型可以最大限度地重用代码、保护类型的安全以及提高 性能。泛型特性对Java影响最大是集合框架的使用。
为了理解什么是泛型,请大家先看一个使用集合的示例:
package 泛型.list泛型集合方法; import java.util.ArrayList; import java.util.List; //list泛型集合方法 public class HelloWorld { public static void main(String[] args) { List list = new ArrayList(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); // list.add(5); 发生编译错误 // for遍历 for (Object item:list){ //1 Integer element = (Integer)item; //2 System.out.println(item); } } }
上述代码实现的功能很简单,就将一些数据保存到集合中,然后再取出。但对于Java 5之前程序员而 言,使用集合经常会面临一个很尴尬的问题:放入一个种特定类型,但是取出时候全部是Object类 型,于是在具体使用时候需要将元素转换为特定类型。上述代码第1行取出的元素是Object类型,在 代码第2行需要强制类型转换。强制类型转换是有风险的,如果不进行判断就臆断进行类型转换会发 生ClassCastException异常。本例代码第2行就会发生了这个异常,JVM会抛出异常,打印出如下的异 常堆栈跟踪信息。
在Java 5之前没有好的解决办法,在类型转换之前要通过instanceof运算符判断一下,该对象是否是目标类型。而泛型的引入可以将这些运行时异常提前到编译期暴露出来,这增强了类型安全检查。
修改代码如下:
package 泛型.list泛型集合方法; import java.util.ArrayList; import java.util.List; //list泛型集合方法 public class HelloWorld { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); // list.add(5); 发生编译错误 // for遍历 for (String item:list){ // Integer element = (Integer)item; System.out.println(item); } } }
由此可见泛型的作用:
1,类型安全。
泛型的主要目标是提高 Java 程序的类型安全。编译时的强类型检查;通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的程度上验证类型假设。没有泛型,这些假设就只存在于程序员的头脑中(或者如果幸运的话,还存在于代码注释中)。
2,消除强制类型转换。
泛型的一个附带好处是,消除源代码中的许多强制类型转换。这使得代码更加可读,并且减少了出错机会。
3,潜在的性能收益。
泛型为较大的优化带来可能。在泛型的初始实现中,编译器将强制类型转换(没有泛型的话,程序员会指定这些强制类型转换)插入生成的字节码中。但是更多类型信息可用于编译器这一事实,为未来版本的 JVM 的优化带来可能。由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改。所有工作都在编译器中完成,编译器生成类似于没有泛型(和强制类型转换)时所写的代码,只是更能确保类型安全而已。 Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
4、更好的代码复用性,比如实现泛型算法
在框架设计时候,BaseDao<T>、BaseService<T>、BaseDaoImpl<T>、BaseServiceImpl<T>;通过继承,实现抽象了所有公共方法,避免了每次都要写相同的代码。
二、使用泛型
泛型对于Java影响最大就是集合了,Java 5之后所有的集合类型都可以有泛型类型,可以限定存放到集 合中的类型。打开API文档,会发现集合类型后面会有,如:Collection、 List、ArrayList、Set和Map,这说明这些类型是支持泛型的。尖括号中的E、K和V 等是类型参数名称,它们是实际类型的占位符。
(1)List泛型集合
代码如下:
package 泛型.list泛型集合方法; import java.util.ArrayList; import java.util.List; //list泛型集合方法 public class HelloWorld { public static void main(String[] args) { List<String> list = new ArrayList<String>(); list.add("1"); list.add("2"); list.add("3"); list.add("4"); // list.add(5); 发生编译错误 // for遍历 for (String item:list){ // Integer element = (Integer)item; System.out.println(item); } } }
(2)Set泛型集合
代码如下:
import java.util.HashSet; import java.util.Iterator; import java.util.Set; //泛型的使用 // 测试Set泛型集合方法 public class HelloWorldGen { public static void main(String[] args) { Set<String> set = new HashSet<String>(); // 向集合中添加元素 set.add("A"); set.add("D"); set.add("E"); // 1.使用for-each循环遍历 System.out.println("--1.使用for-each循环遍历--"); for (String item :set){ System.out.println(item); } // 2.使用迭代器遍历 System.out.println("--2.使用迭代器遍历--"); Iterator<String> iterator = set.iterator(); while (iterator.hasNext()){ String item = iterator.next(); System.out.println("读取集合元素"+item); } } }
(3)Map泛型集合
代码如下:
import java.util.*; //Map泛型集合 public class HelloWorldGen { public static void main(String[] args) { Map<Integer,String> map = new HashMap<Integer, String>(); map.put(101,"A"); map.put(102, "B"); map.put(103, "C"); map.put(104, "D"); // 1.使用for-each循环遍历 System.out.println("--1.使用for-each循环遍历--"); // 获得键集合 Set<Integer> keys = map.keySet(); for (Integer key:keys ){ String value = map.get(key); System.out.printf("key=%d - value=%s \n", key, value); } // 2.使用迭代器遍历集合 System.out.println("--2.使用迭代器遍历--"); // 获得值集合 Collection<String> values = map.values(); // 遍历值集合 Iterator<String> iterator = values.iterator(); while (iterator.hasNext()){ String item = iterator.next(); System.out.println("读取集合元素"+item); } } }
三、自定义泛型类
根据自己的需要也可以自定义泛型类、泛型接口和带有泛型参数的方法。下面通过一个示例介绍一下 泛型类。数据结构中有一种“队列”(queue)数据结构
代码如下:
import java.util.ArrayList; import java.util.List; /** * 自定义的泛型队列集合 * */ //自定义泛型类 public class Queue<T> { // 声明保存队列元素集合items private List<T> items; // 构造方法初始化是集合items public Queue(){ this.items = new ArrayList<T>(); } /** * 入队方法 * @param item 参数需要入队的元素 */ public void queue(T item){ this.items.add(item); } /** * 出队方法 * @return 返回出队元素 */ public T dequeue(){ if(items.isEmpty()){ return null; } else { return this.items.remove(0); } } @Override public String toString(){ return items.toString(); } }
测试代码:
public class Helloworld{ public static void main(String[] args) { Queue<String> genericQueue = new Queue<String>(); genericQueue.queue("A"); genericQueue.queue("C"); genericQueue.queue("B"); genericQueue.queue("D"); //genericQueue.queue(1);//编译错误 // 打印队列 System.out.println(genericQueue); // 队列出列 genericQueue.dequeue(); // 打印队列 System.out.println(genericQueue); } }
运行结果:
[A, C, B, D] [C, B, D]
三、自定义泛型接口与泛型方法
(1)自定义泛型接口
自定义泛型接口与自定义泛型类类似,定义的方式完全一样。
package 泛型.测试自定义泛型队列; //自定义泛型接口 /** * 自定义的泛型队列集合 * */ public interface IQueue<T> { /** * 入队方法 * @param item 参数需要入队的元素 */ public void queue(T item); /** * 出队方法 * @return 返回出队元素 */ public T dequeue(); }
实现这接口IQueue具体方式有很多,可以是List、Set或Hash等多种不同方式,下面笔者给出一个基 于List实现方式,代码如下:
package 泛型.测试自定义泛型队列; import java.util.ArrayList; import java.util.List; /** * 自定义的泛型队列集合 * */ public class ListQueue<T> implements IQueue<T> { // 声明保存队列元素集合items private List<T> items; // 构造方法初始化集合items public ListQueue(){ this.items = new ArrayList<T>(); } /** * 入队方法 * * @param item * 参数需要入队的元素 */ @Override public void queue(T item) { this.items.add(item); } /** * 出队方法 * * @return 返回出队元素 */ @Override public T dequeue() { if (items.isEmpty()) { return null; } else { return this.items.remove(0); } } @Override public String toString(){ return items.toString(); } }
测试代码:
package 泛型.测试自定义泛型队列; //测试自定义泛型队列 public class hello { public static void main(String[] args) { ListQueue<String> genericQueue = new ListQueue<String>(); genericQueue.queue("A"); genericQueue.queue("C"); genericQueue.queue("B"); genericQueue.queue("D"); //genericQueue.queue(1);//编译错误 // 打印队列 System.out.println(genericQueue); // 队列出列 genericQueue.dequeue(); // 打印队列 System.out.println(genericQueue); } }
运行结果:
[A, C, B, D] [C, B, D]
(2)泛型方法
在方法中也可以使用泛型,即方法的参数类型或返回值类型,可以用类型参数表示。
泛型的类型参数也可以限定一个边界,例如比较方法isEquals()只想用于数值对象大小的比较, 实现代码如下:
package 泛型.泛型方法; //泛型方法 public class HelloWorld { public static void main(String[] args) { System.out.println(isEquals(new Integer(1),new Integer(5))); // 发生了自动封装 System.out.println(isEquals(1,5)); System.out.println(isEquals(new Double(1.0),new Double(1.0))); // 发生了自动封装 System.out.println(isEquals(1.0,1.0)); // 如果不注释以下代码就会报错,因为下面的限定是Number类型 // System.out.println(isEquals("A","A")); } // 限定参数类型为Number // 如果不加extends Number 即他们是任何引用类型,返回值是<T> public static <T extends Number> boolean isEquals(T a,T b){ //2 return a.equals(b); } }
上述代码第2行定义泛型使用语句,该语句是限定类型参数只能是Number类型。 所以代码第1行的试图传递String类型的参数,则会发生编译错误。