java泛型指導手冊(part1)
index
大致瞭解一下
java中的泛型List(Generic List in Java)
Java中的泛型集合(Generic Set in Java)
java中的泛型Map(Generic Map in Java)
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 Set
和 value 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.