泛型( Generic)实现了参数化类型的概念,使得代码可以应用于多种类型。我们常用的ArrayList<T>
,HashMap<K,V>
等都是使用了泛型。泛型的意思就是可以适用于许多种的类型。
Java的核心概念就是:告诉编译器想使用什么类型,然后编译器帮你处理一切细节。
泛型类
我们最常见的就是泛型类了,比如我们上面提到的ArrayList<T>
,HashMap<K,V>
等等。下面我们以一个具体的例子,看看如何创建一个自己的泛型类。
创建一个堆栈类,实现简单的入栈和出栈操作。
package genericdemo.classdemo;
/**
* 利用泛型实现一个简单的堆栈类
* @author nxiangbo
*
* @param <T>
*/
public class LinkedStack<T> {
/**
* 内部类Node是一个泛型。
* @author nxiangbo
*
* @param <E>
*/
private static class Node<E>{
E item;
Node<E> next;
public Node() {
item = null;
next = null;
}
public Node(E item, Node<E> next){
this.item = item;
this.next = next;
}
private boolean end() {
return (item==null)&&(next==null);
}
}
private Node<T> top = new Node<T>();
/**
* 入栈
* @param item
*/
public void push(T item){
top = new Node<T>(item, top);
}
/**
* 出栈
* @return
*/
public T pop(){
T result = top.item;
if(!top.end()){
top = top.next;
}
return result;
}
}
下面测试一下上述泛型类如何使用的。
public static void main(String[] args) {
//String 类型的
LinkedStack<String> lsStr = new LinkedStack<String>();
String strs = "this is generic demo";
for (String string : strs.split(" ")) {
lsStr.push(string);
}
System.out.println(lsStr.pop());
//int类型的
LinkedStack<Integer> lsInt = new LinkedStack<>();
int[] nums = {12,43,54,23,43,12345};
for (int i : nums) {
lsInt.push(i);
}
System.out.println(lsInt.pop());
泛型接口
泛型接口
泛型也可以应用于接口。例如生成器(Generator),这是一种专门负责创建对象的类。实际上这是工厂方法设计模式中的一种应用。
一般而言,一个生成器只定义一个方法,该方法用于产生新的对象。
接口的定义
package genericdemo.interfacedemo;
/**
* 泛型接口
* @author nxiangbo
*
*/
public interface Generator<T> {
//返回的是参数类型`T`
T next();
}
那么,该如何使用泛型接口呢??
我们以斐波那契数为例子,来说明一下,泛型接口如何使用。
public class Fibonacci implements Generator<Integer>{
private int count = 0;
private int fib(int n){
if(n <2){
return 1;
}
return fib(n-2)+fib(n-1);
}
@Override
public Integer next() {
return fib(count++);
}
public static void main(String[] args) {
Fibonacci finGen = new Fibonacci();
for (int i = 0; i < 8; i++) {
System.out.print(finGen.next()+", ");
}
}
}
by the way,基本类型无法做为类型参数。比如上面的Generator<Integer>,不能写成Generator<int>
, 以及ArrayList<Integer>
,不能写成ArrayList<int>
等等。
泛型方法
到目前为止,我们都是将泛型应用在类中,但也可以在类中包含参数化方法,而这个方法可以是泛型类,也可以不是泛型类。
泛型方法使得该方法能够独立于类而产生变化。一个基本原则是:无论何时,只要能够使用泛型,就应该尽量使用泛型。如果使用泛型方法可以取代泛型类,那么就应该只使用泛型方法。另外,对于一个static方法,无法访问泛型类的类型参数,所以,如果static方法需要使用泛型能力,就必须使用泛型方法。
泛型方法的定义:只需将泛型参数列表置于返回值之前。
泛型方法与泛型类不同的是: 泛型类在被使用时,必须指明参数类型。而泛型方法在被使用时,不必指明参数类型,因为编译器会为我们找出具体的类型,这称为类型参数推断(type argument inference)。
对于泛型类,由于编译器不能够从泛型参数列表中的一个参数推断出另一个参数。所以,我们每次使用Map等泛型时,必须这样,Map<String,String> map = new HashMap<String,String>
。增加了很多的代码。因此,我们可以使用泛型方法构建一个工具类,用以简化泛型类的使用。
代码如下。
package genericdemo.methoddemo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.Set;
/**
* 泛型方法
* 可作为一个工具类,去简化HashMap<K,V>泛型的使用
* @author nxiangbo
*
*/
public class New {
public static <K,V> Map<K, V> map(){
return new HashMap<K, V>();
}
public static <T> java.util.List<T> list(){
return new ArrayList<T>();
}
public static <T> LinkedList<T> linkedList(){
return new LinkedList<>();
}
public static <T> Queue<T> queue(){
return new LinkedList<>();
}
public static <T> Set<T> set(){
return new HashSet<T>();
}
}
有了这个工具类后,我们就可以很简单的声明泛型了。
具体使用如下。
public static void main(String[] args) {
Map<String, String> map = New.map();
java.util.List<String> list = New.list();
LinkedList<String> linkedList = New.linkedList();
}
下面我们再看一个关于泛型方法的例子。利用泛型方法,实现集合的并、交、补、差集的工具类。
代码如下。
package genericdemo.methoddemo;
import java.util.HashSet;
import java.util.Set;
/**
* Sets工具类,实现集合的并、交、差、补集
* @author nxiangbo
*
*/
public class Sets {
/**
* 并集
* @param set1
* @param set2
* @return
*/
public static <T> Set<T> union(Set<T> set1, Set<T> set2){
Set<T> result = new HashSet<T>(set1);
result.addAll(set2);
return result;
}
/**
* 交集
* @param set1
* @param set2
* @return
*/
public static <T> Set<T> intersection(Set<T> set1, Set<T> set2){
Set<T> result = new HashSet<T>(set1);
result.retainAll(set2);
return result;
}
/**
* 差集
* @param set1
* @param set2
* @return
*/
public static <T> Set<T> difference(Set<T> set1, Set<T> set2){
Set<T> result = new HashSet<T>(set1);
result.removeAll(set2);
return result;
}
/**
* 补集
* @param set1
* @param set2
* @return
*/
public static <T> Set<T> complement(Set<T> set1, Set<T> set2){
return difference(union(set1, set2), intersection(set1, set2));
}
}
测试如下。
public static void main(String[] args) {
Set<String> set1 = new HashSet<String>();
set1.add("hash");
set1.add("map");
set1.add("list");
Set<String> set2 = new HashSet<String>();
set2.add("hash");
set2.add("array");
set2.add("list");
System.out.println(union(set1, set2));
System.out.println(intersection(set1, set2));
System.out.println(difference(set1, set2));
}