Java™ 教程(泛型類型)

泛型類型

泛型類型是經過類型參數化的泛型類或接口,修改如下Box類以演示此概念。segmentfault

一個簡單的Box類

首先檢查一個對任何類型的對象進行操做的非泛型Box類,它只須要提供兩個方法:set,它將一個對象添加到box中,get,它將檢索它:數組

public class Box {
    private Object object;

    public void set(Object object) { this.object = object; }
    public Object get() { return object; }
}

因爲它的方法接受或返回一個Object,因此你能夠自由地傳入任何你想要的東西,前提是它不是一種原始類型,沒法在編譯時沒有辦法驗證如何使用該類,代碼的一部分可能會在box中放置一個Integer並指望從中獲取Integer,而代碼的另外一部分可能會錯誤地傳入String,從而致使運行時錯誤。框架

Box類的泛型版本

泛型類使用如下格式定義:函數

class name<T1, T2, ..., Tn> { /* ... */ }

由尖括號(<>)分隔的類型參數部分跟在類名後面,它指定類型參數(也稱爲類型變量)T1, T2, ...Tnthis

要更新Box類以使用泛型,能夠經過將代碼「public class Box」更改成「public class Box <T>」來建立泛型類型聲明,這引入了類型變量T,能夠在類中的任何位置使用。編碼

經過此更改,Box類變爲:code

/**
 * Generic version of the Box class.
 * @param <T> the type of the value being boxed
 */
public class Box<T> {
    // T stands for "Type"
    private T t;

    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

如你所見,全部出現的Object都被T替換,類型變量能夠是你指定的任何非基本類型:任何類類型、任何接口類型、任何數組類型,甚至是其餘類型變量。對象

能夠應用相同的技術來建立泛型接口。接口

類型參數命名約定

按照慣例,類型參數名稱是單個大寫字母,這與你已經瞭解的變量命名約定造成鮮明對比,而且有充分的理由:若是沒有這種約定,就很難區分類型變量和普通類或接口名稱。開發

最經常使用的類型參數名稱是:

  • E — 元素(Java集合框架普遍使用)
  • K — 鍵
  • N — 數
  • T — 類型
  • V — 值
  • S、U、V等 — 第2、第3、第四種類型

你將在整個Java SE API和本課程的其他部分中看到這些名稱。

調用和實例化泛型類型

要從代碼中引用泛型Box類,必須執行泛型類型調用,它將T替換爲某些具體值,例如Integer

Box<Integer> integerBox;

你能夠將泛型類型調用視爲與普通方法調用相似,但不是將參數傳遞給方法,而是將類型參數(在本例中爲Integer)傳遞給Box類自己。

類型參數和類型參數術語:許多開發人員互換地使用術語「類型參數」和「類型實參」,但這些術語並不相同,編碼時,提供類型實參以建立參數化類型,所以, Foo<T>中的 T是類型參數,而 Foo<String> f中的 String是類型實參,本課程在使用這些術語時會遵循此定義。

與任何其餘變量聲明同樣,此代碼實際上並不建立新的Box對象,它只是聲明integerBox將保存對「Box of Integer」的引用,這就是Box<Integer>的讀取方式。

泛型類型的調用一般稱爲參數化類型。

要實例化此類,請像往常同樣使用new關鍵字,但在類名和括號之間放置<Integer>

Box<Integer> integerBox = new Box<Integer>();

菱形

在Java SE 7及更高版本中,只要編譯器能夠從上下文中肯定或推斷類型參數,就能夠用一組空的類型參數(<>)替換調用泛型類的構造函數所需的類型參數,這對尖括號<>非正式地稱爲菱形,例如,你可使用如下語句建立Box<Integer>的實例:

Box<Integer> integerBox = new Box<>();

有關菱形表示法和類型推斷的更多信息,請參閱類型推斷。

多個類型參數

如前所述,泛型類能夠有多個類型參數,例如,OrderedPair泛型類,它實現了Pair泛型接口:

public interface Pair<K, V> {
    public K getKey();
    public V getValue();
}

public class OrderedPair<K, V> implements Pair<K, V> {

    private K key;
    private V value;

    public OrderedPair(K key, V value) {
    this.key = key;
    this.value = value;
    }

    public K getKey()    { return key; }
    public V getValue() { return value; }
}

如下語句建立OrderedPair類的兩個實例:

Pair<String, Integer> p1 = new OrderedPair<String, Integer>("Even", 8);
Pair<String, String>  p2 = new OrderedPair<String, String>("hello", "world");

代碼new OrderedPair<String, Integer>,將K實例化爲String,將V實例化爲Integer,所以,OrderedPair的構造函數的參數類型分別是StringInteger,因爲自動裝箱,將Stringint傳遞給類是有效的。

正如菱形中所提到的,由於Java編譯器能夠從聲明OrderedPair<String, Integer>推斷出KV類型,因此可使用菱形表示法縮短這些語句:

OrderedPair<String, Integer> p1 = new OrderedPair<>("Even", 8);
OrderedPair<String, String>  p2 = new OrderedPair<>("hello", "world");

要建立泛型接口,請遵循與建立泛型類相同的約定。

參數化類型

你還可使用參數化類型(即List<String>)替換類型參數(即KV),例如,使用OrderedPair<K, V>示例:

OrderedPair<String, Box<Integer>> p = new OrderedPair<>("primes", new Box<Integer>(...));

上一篇:爲何要使用泛型?

下一篇:泛型原始類型

相關文章
相關標籤/搜索