1. 程式人生 > >教你如何使用Java泛型

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