Java SE面向物件--13.Java泛型
阿新 • • 發佈:2018-12-12
1、概述
在前面學習集合時,我們都知道集合中是可以存放任意物件的,只要把物件儲存集合後,那麼這時他們都會被提升成Object型別。當我們在取出每一個物件,並且進行相應的操作,這時必須採用型別轉換。
public class GenericDemo { public static void main(String[] args) { Collection coll = new ArrayList(); coll.add("abc"); coll.add("itcast"); coll.add(5);//由於集合沒有做任何限定,任何型別都可以給其中存放 Iterator it = coll.iterator(); while(it.hasNext()){ //需要列印每個字串的長度,就要把迭代出來的物件轉成String型別 String str = (String) it.next(); System.out.println(str.length()); } } }
程式在執行時發生了問題java.lang.ClassCastException。怎麼解決這個問題 呢、下面就來說說泛型
泛型:可以在類或方法中預支地使用未知的型別。用來靈活地將資料型別應用到不同的類、方法、介面當中。將資料型別作為引數進行傳遞。
好處:
-
將執行時期的ClassCastException,轉移到了編譯時期變成了編譯失敗。
-
避免了型別強轉的麻煩。
2、定義泛型類
格式:
修飾符 class 類名<代表泛型的變數> { }
// 自定義一個泛型類, 泛型的標誌是 <> // E (Element) T (Type) K (Key) V (Value) R (Result) // 請問 : 泛型類中的泛型在何時進行確定 ??? 在例項化該物件時, 進行泛型真實型別的確定. // 泛型類中泛型是為本類的屬性和方法提供的. ArrayList<E> public class GenericClass1<T> { // 屬性 private T field; // 方法 : public void setField(T field) { this.field = field; } public T getField() { return field; } }
演示程式碼:
public class TestGenericClass1 { public static void main(String[] args) { // 1. 建立一個泛型類物件 GenericClass1<String> g1 = new GenericClass1<>(); g1.setField("hello Java."); String str = g1.getField(); System.out.println(str); GenericClass1<Integer> g2 = new GenericClass1<>(); g2.setField(998); Integer number = g2.getField(); System.out.println(number); } }
3、定義泛型介面
格式:
修飾符 interface介面名<代表泛型的變數> { }
// 定義了一個泛型介面 : 類可以在例項化同時確定類上的泛型, 而介面不能例項化, 何時確定泛型呢 ??? 實現類來確定.
public interface MyGenericInterface<T> {
// 抽象方法
void method(T t);
}
演示程式碼:
介面程式碼一:
// 確定介面中泛型的第一種方式, 實現類在實現該介面的同時進行確定.
public class MyGenericInterfaceImpl1 implements MyGenericInterface<String> {
@Override
public void method(String s) {
System.out.println(s);
}
}
介面程式碼二:
// 確定介面中泛型的第二種方式, 當實現類實現該介面時, 不確定該介面的泛型, 此時, 該類必須定義為 `泛型類`.
// 泛型類在例項化物件時, 就可以確定泛型的真實型別, 此時, 該型別就可以被傳遞介面的泛型中, 從而來確定介面的泛型.
public class MyGenericInterfaceImpl2<T> implements MyGenericInterface<T> {
@Override
public void method(T t) {
System.out.println(t);
}
}
測試類:
public class TestMyGenericInterface {
public static void main(String[] args) {
// 1. 實現類 MyGenericInterfaceImpl1
MyGenericInterfaceImpl1 g1 = new MyGenericInterfaceImpl1();
g1.method("你好, 上海.");
// ArrayList<E> 實現了 List<E> 介面.
// 2. 實現類 MyGenericInterfaceImpl2
MyGenericInterfaceImpl2<String> g2 = new MyGenericInterfaceImpl2<>();
g2.method("hello ShangHai.");
MyGenericInterfaceImpl2<Integer> g3 = new MyGenericInterfaceImpl2<>();
g3.method(998);
}
}
4、定義泛型方法
格式:
修飾符 <代表泛型的變數> 返回值型別 方法名(引數){ }
程式碼:
public class GenericMethodTest {
public static void main(String[] args) {
printInfo(new Person("張三", 18));
printInfo(new Animal("哮天犬", 38));
}
// 定義一個方法 : 輸出 `物件資訊` (Person, Animal) -> Object
// 泛型必須先定義, 後使用. 泛型使用 <> 尖括號定義. 位置: 修飾符 <T> 返回值型別 方法名(引數列表) { 方法體 }
public static <T> void printInfo(T t) {
System.out.println(t);
}
}
5、泛型萬用字元:?
說明:當使用泛型類或者介面時,傳遞的資料中,泛型型別不確定,可以通過萬用字元<?>表示。但是一旦使用泛型的萬用字元後,只能使用Object類中的共性方法,集合中元素自身方法無法使用。
使用場景 : 必須在已經定義的介面或類上的泛型位置才能使用.
演示:
public class GenericMethodTest {
public static void main(String[] args) {
ArrayList<Person> list1 = new ArrayList<>();
list1.add(new Person("張三", 18));
list1.add(new Person("李四", 19));
list1.add(new Person("王五", 20));
printCollection(list1);
ArrayList<Animal> list2 = new ArrayList<>();
list2.add(new Animal("哮天犬1號", 100));
list2.add(new Animal("哮天犬2號", 200));
printCollection(list2);
}
// 需求 : 定義一個方法, 輸出集合中所有元素的資訊.
// ? 泛型的萬用字元, 好處, 不要宣告.
// 預設 ? extends Object
// 翻譯 : ? 表示我不知道這是什麼型別, 但是, 我知道, 它可以是 Object 型別, 或者該型別 繼承 Object 型別.
public static void printCollection(Collection<?> c) {
for (Object obj : c){
System.out.println(obj);
}
}
/*public static <T> void printCollection(Collection<T> c) {
for (T t : c) {
System.out.println(t);
}
}*/
}
泛型上下限:
泛型的上限:
格式: 型別名稱 <? extends 類 > 物件名稱
意義: 只能接收該型別及其子類
泛型的下限:
格式: 型別名稱 <? super 類 > 物件名稱
意義: 只能接收該型別及其父型別
上限:
public class Test {
public static void main(String[] args) {
ArrayList<Person> list1 = new ArrayList<>();
printCollection(list1);
ArrayList<Student> list2 = new ArrayList<>();
printCollection(list2);
ArrayList<Teacher> list3 = new ArrayList<>();
printCollection(list3);
// ArrayList<Animal> list4 = new ArrayList<>();
// printCollection(list4);
}
// 定義一個方法 :
public static void printCollection(Collection<? extends Person> c) {
for (Person p : c) {
System.out.println(p);
}
}
}
下限:
public class Test {
public static void main(String[] args) {
ArrayList<ArmyDog> list1 = new ArrayList<>();
printCollection(list1);
ArrayList<Dog> list2 = new ArrayList<>();
printCollection(list2);
ArrayList<Animal> list3 = new ArrayList<>();
printCollection(list3);
/*
ArrayList<Cat> list4 = new ArrayList<>();
printCollection(list4);
ArrayList<Wolf> list5 = new ArrayList<>();
printCollection(list5);
*/
}
// 方法 :
public static void printCollection(Collection<? super ArmyDog> c) {
for (Object obj : c) {
System.out.println(obj);
}
}
}
總結:
注意 : 如果泛型不指定, 那麼該泛型就會被提升為 Object 型別.
好處1 : 統一了集合中元素型別.
好處2 : 避免了型別強轉問題.
好處3 : 將執行時期的型別轉換異常, 提前到編譯時期進行語法檢查.