教你如何使用Java泛型
泛型( 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));
}