1. 程式人生 > >java泛型指導手冊(part1)

java泛型指導手冊(part1)

index

  1. 大致瞭解一下

  2. java中的泛型List(Generic List in Java)

  3. Java中的泛型集合(Generic Set in Java)

  4. java中的泛型Map(Generic Map in Java)

  5. java的泛型化類

1.大致瞭解一下

從java5開始,泛型就被加入了java。泛型的意義在於:在你操作object之前,提供了一種指定特定型別為更加通用目的的類或方法的手段。聽起來很抽象吧?來點例子看看。

注:泛型當然可以用在不是collection的其他類上,但是呢,用collection來展示泛型的基本概念是很好的。

泛型的例子

List這個介面代表一列表object例項,這意味著我們可以在List裡放任何Object,比如:

List list = new ArrayList();
list.add(new Integer(2));
list.add("a String");

因為任何object都可能被加入進來,所以你不得不進行型別轉換。比如:

Integer integer = (Integer) list.get(0);
String string   = (String) list.get(1);

很經常的一個情況是,你在一個List裡僅新增一種型別資料進去,比如String或其他東西,而不是像我在上面做的一樣,把integer和String混在一起。

於是,你使用泛型這一特性,可以限制放在List裡的object。除此之外,你省去了型別轉換的煩惱。看一下用泛型的真正例子:


List<String> strings = new ArrayList<String>();
strings.add("a String");
String aString = strings.get(0);

牛逼吧?

java7裡的型別猜測

泛型特性在java7裡做了升級。從7開始,java編譯器可以從你賦值給的那個變數的資訊知道collection的型別,這裡是java7裡的泛型例子:

List<String
> strings = new ArrayList<>();

注意看,ArrayList的泛型型別已經被省略了,僅有<>留了下來。有時候,這東西也叫做菱形操作符。當你寫一個菱形操作符作為一個泛型型別,java的編譯器會假設這個被初始化的類和它賦值給的那個變數(i.e.左邊的變數)有相同的型別。在上面的例子,String就是泛型的型別,應為List變數把String設定為了它的型別。

迴圈裡的泛型

java5有一個新的for迴圈,叫做for-each, 這東西對於制定泛型的collection處理的很好。來個例子:


List<String> strings = new ArrayList<String>();

//... add String instances to the strings list...

for(String aString : strings){
  System.out.println(aString);
}

這個for-each迴圈迭代在strings列表裡的所有String例項。

每次迭代,下一個String例項就賦值給aString變數。這個for-loop迴圈比原來的while迴圈更加精小,在while裡你不得不迭代地使用collection的迭代器(Iterator)的Iterator.next()方法去獲得下個例項,這導致程式碼臃腫。

泛型之於非collection的其他型別

你當然可以將泛型用於其他class,而非僅僅是collection。你可以將你自己的class泛型化。這個以後再說細節。

2. java中的泛型List(Generic List in Java)

java的List介面(java.util.List)能夠被泛型化。這就是說,List的一個例項能夠被給定一個型別,所以只有那種型別的例項能插入List中和從List中被讀取,這裡就是一個例子:

List<String> list = new ArrayList<String>;

這個list現在僅僅和String型別的例項聯絡,這意味著你只能把String放進這個list。不要放別的進list,編譯器會抱怨。

泛型的型別檢查,僅僅在編譯期檢查。在執行時你可以調整程式碼,使字串列表具有字串插入的其他物件。不過這是個壞主意。(At runtime it is possible to tweak your code so that a String List has other objects that String’s inserted. This is a bad idea, though.)

進入一個泛型的列表

如下,你能get和insert泛型list的元素:

List<String> list = new ArrayList<String>;

String string1 = "a string";
list.add(string1);

String string2 = list.get(0);

注意,沒必要對List.get()方法中得到的物件進行轉型,雖然平常需要。

編譯器已經知道這個List只包含String的例項,所以轉型(cast)是沒有必要的。

在泛型化的列表中的迭代

你可以使用迭代器迭代泛型列表,如下所示:

List<String> list = new ArrayList<String>;

Iterator<String> iterator = list.iterator();

while(iterator.hasNext()){
  String aString = iterator.next();
}

跟上面的進入一個泛型的列表裡一樣,不必轉型,在while的跌代器裡,你也不必轉型。

你也可以使用新的for-loop,如下:


List<String> list = new ArrayList<String>;

for(String aString : list) {
    System.out.println(aString);
}

注意,現在有一個String物件,aString,在for-loop的括號裡被宣告。

每一次的迭代,這個aString都獲得了list的當前元素。

3. Java中的泛型集合(Generic Set in Java)

Java的Set介面(java.util.Set)可以被泛型化。也就是說,Set的例項能夠被給定一個型別,所以僅有那種型別的例項能在Set中插入,讀取。例子如下:

Set<String> set = new HashSet<String>;

這個集合現在僅僅與String型別聯絡起來了,意味著,只有String型別的例項能夠放入這個set裡,不要放別的進set,編譯器會抱怨。

泛型的型別檢查,僅僅在編譯期檢查。在執行時你可以調整程式碼,使字串集合(Set)具有字串插入的其他物件。不過這是個壞主意。(At runtime it is possible to tweak your code so that a String Set has other objects that String’s inserted. This is a bad idea, though.)

為泛型化的集合新增元素

通常使用add(),一如你平常也常用的


Set<String> set = new HashSet<String>;

String string1 = "a string";
set.add(string1);

那麼最大的差別是什麼呢?好吧,就是你嘗試新增非String的例項進入上述Set,編譯器會抱怨。這是個很好的額外的型別檢查特性,你值得擁有。

迭代一個泛型化的Set

使用一個iterator(迭代器)吧,如下:

Set<String> set = new HashSet<String>;

Iterator<String> iterator = set.iterator();

while(iterator.hasNext()){
  String aString = iterator.next();
}

請注意,就像之前在List那一節所說一樣,不必為interator.next()去轉型。

因為Set已經被泛型化了(泛型化的意思是有了一個指定的type),編譯器知道為你打理一切,不論你使用iterator還是for-loop.

下面就為你展示使用for-loop


Set<String> set = new HashSet<String>;

for(String aString : set) {
    System.out.println(aString);
}

4. java中的泛型Map(Generic Map in Java)

Map這個介面(java.util.Map)也是能夠被泛型化的,這就是說,在一個泛型化的Map中,你可以指定為key-value指定特定的型別,這裡是例子:

Map<Integer, String> set = new HashMap<Integer, String>;

這個Map例項set,現在只能接受Integer作為keys,接受String作為values

一樣地,泛型的型別檢查僅僅發生在編譯期。你可以在執行期調整你的程式碼,這樣你的其他例項就能插入了(我翻譯時這段話總是不太確定,是能夠動態改變key-value對應的原來的Integer-String型別嗎?)。儘管這是一個壞主意。

進入(讀取)一個泛型化的Map

新增和獲得一個泛型化的Map的元素,一如往常,你可以使用put(),get()

Map<Integer, String> map = new HashMap<Integer, String>;

Integer key1   = new Integer(123);
String  value1 = "value 1";

map.put(key1, value1);

String value1_1 = map.get(key1);

所以大的改變在哪呢?(作者應該是在將新的java7特性羞辱舊版java4,我說可能啊)。

在於一個很nice的型別檢查:對於上訴例子,當你的key-value對不是Integer-String時,編譯器會抱怨。

java5的auto boxing特效能給我們帶來很大方便(使得我們的程式碼敘述更加人性化):

Map<Integer, String> map = new HashMap<Integer, String>;

Integer key1   = 123;
String  value1 = "value 1";

map.put(key1, value1);

//or

map.put(123, value1);


String value1_1 = map.get(123);

迭代一個泛型化的Map

一個Map,有兩個collection可以被迭代: key Setvalue Set

通常情況下,你迭代key Set,然後通過Map.get(key)來獲取value。

來,給你兩個例子:


Map<Integer, String> map = new HashMap<Integer, String>;

//... add key, value pairs to the Map

// iterate keys.
Iterator<Integer> keyIterator   = map.keySet().iterator();

while(keyIterator.hasNext()){
  Integer aKey   = keyIterator.next();
  String  aValue = map.get(aKey);
}


Iterator<String>  valueIterator = map.values().iterator();

while(valueIterator.hasNext()){
  String aString = valueIterator.next();
}

第二個例子


Map<Integer, String> map = new HashMap<Integer, String>;

//... add key, value pairs to the Map

for(Integer aKey : map.keySet()) {
    String aValue = map.get(aKey);
    System.out.println("" + aKey + ":" + aValue);
}

for(String aValue : map.values()) {
    System.out.println(aValue);
}

同理,不必進行型別檢查,編譯器知道key的型別一定是Integer,value的值一定是String.

5.java的泛型化類