Java學習_泛型
- 什麼是泛型。
- Java標準庫提供的
ArrayList
內部就是一個Object[]
數組,配合存儲一個當前分配的長度,就可以充當「可變數組」。public class ArrayList { private Object[] array; private int size; public void add(Object e) {...} public void remove(int index) {...} public Object get(int index) {...} }
-
如果用上述
ArrayList
存儲String
類型,會有這麼幾個缺點:-
需要強制轉型;
-
不方便,易出錯。
- 程式碼必須這麼寫:
ArrayList list = new ArrayList(); list.add("Hello"); // 獲取到Object,必須強制轉型為String: String first = (String) list.get(0);
很容易出現ClassCastException,因為容易「誤轉型」。
list.add(new Integer(123)); // ERROR: ClassCastException: String second = (String) list.get(1);
要解決上述問題,我們可以為
String
單獨編寫一種ArrayList。
public class StringArrayList { private String[] array; private int size; public void add(String e) {...} public void remove(int index) {...} public String get(int index) {...} }
這樣一來,存入的必須是
String
,取出的也一定是String
,不需要強制轉型,因為編譯器會強制檢查放入的類型。StringArrayList list = new StringArrayList(); list.add("Hello"); String first = list.get(0); // 編譯錯誤: 不允許放入非String類型: list.add(new Integer(123));
問題暫時解決。然而,新的問題是,如果要存儲
Integer
,還需要為Integer
單獨編寫一種ArrayList。實際上,還需要為其他所有class單獨編寫一種
ArrayList。
這是不可能的,JDK的class就有上千個,而且它還不知道其他人編寫的class。
為了解決新的問題,我們必須把
ArrayList
變成一種模板:ArrayList<T>
public class ArrayList<T> { private T[] array; private int size; public void add(T e) {...} public void remove(int index) {...} public T get(int index) {...} }
T
可以是任何class。這樣一來,我們就實現了:編寫一次模版,可以創建任意類型的ArrayList。
// 創建可以存儲String的ArrayList: ArrayList<String> strList = new ArrayList<String>(); // 創建可以存儲Float的ArrayList: ArrayList<Float> floatList = new ArrayList<Float>(); // 創建可以存儲Person的ArrayList: ArrayList<Person> personList = new ArrayList<Person>();
這樣一來,既實現了編寫一次,萬能匹配,又通過編譯器保證了類型安全:這就是泛型。
-
向上轉型
- 在Java標準庫中的
ArrayList<T>
實現了List<T>
介面,它可以向上轉型為List<T>。
public class ArrayList<T> implements List<T> { ... } List<String> list = new ArrayList<String>();
類型
ArrayList<T>
可以向上轉型為List<T>
。不能把ArrayList<Integer>
向上轉型為ArrayList<Number>
或List<Number>
。ArrayList<Integer>和ArrayList<Number>兩者完全沒有繼承關係。
- Java標準庫提供的
-
使用泛型
-
使用
ArrayList
時,如果不定義泛型類型時,泛型類型實際上就是Object
-
編譯器如果能自動推斷出泛型類型,就可以省略後面的泛型類型。
// 可以省略後面的Number,編譯器可以自動推斷泛型類型: List<Number> list = new ArrayList<>();
-
泛型介面
-
除了
ArrayList<T>
使用了泛型,還可以在介面中使用泛型。例如,Arrays.sort(Object[])
可以對任意數組進行排序,但待排序的元素必須實現Comparable<T>
這個泛型介面。public interface Comparable<T> { /** * 返回負數: 當前實例比參數o小 * 返回0: 當前實例與參數o相等 * 返回正數: 當前實例比參數o大 */ int compareTo(T o); }
可以直接對
String
數組進行排序。// sort import java.util.Arrays; public class Main { public static void main(String[] args) { String[] ss = new String[] { "Orange", "Apple", "Pear" }; Arrays.sort(ss); System.out.println(Arrays.toString(ss)); } }
這是因為
String
本身已經實現了Comparable<String>
介面。如果換成我們自定義的Person
類型試試。1 // sort 2 import java.util.Arrays; 3 4 public class Main { 5 public static void main(String[] args) { 6 Person[] ps = new Person[] { 7 new Person("Bob", 61), 8 new Person("Alice", 88), 9 new Person("Lily", 75), 10 }; 11 Arrays.sort(ps); 12 System.out.println(Arrays.toString(ps)); 13 14 } 15 } 16 17 class Person { 18 String name; 19 int score; 20 Person(String name, int score) { 21 this.name = name; 22 this.score = score; 23 } 24 public String toString() { 25 return this.name + "," + this.score; 26 } 27 }
運行程式,我們會得到
ClassCastException
,即無法將Person
轉型為Comparable
。我們修改程式碼,讓Person
實現Comparable<T>
介面。1 // sort 2 import java.util.Arrays; 3 4 public class Main { 5 public static void main(String[] args) { 6 Person[] ps = new Person[] { 7 new Person("Bob", 61), 8 new Person("Alice", 88), 9 new Person("Lily", 75), 10 }; 11 Arrays.sort(ps); 12 System.out.println(Arrays.toString(ps)); 13 } 14 } 15 class Person implements Comparable<Person> { 16 String name; 17 int score; 18 Person(String name, int score) { 19 this.name = name; 20 this.score = score; 21 } 22 public int compareTo(Person other) { 23 return this.name.compareTo(other.name); 24 } 25 public String toString() { 26 return this.name + "," + this.score; 27 } 28 }
運行上述程式碼,可以正確實現按
name
進行排序。
-
-
未完待續