目錄
1. 為什么使用泛型
2. 泛型的使用方式
2.1.?泛型類?
2.2.?泛型接口
?2.3.?泛型方法
3. 泛型涉及的符號
3.1. 類型通配符"?"?
3.2. 占位符 T/K/V/E
?3.3. 占位符T和通配符?的區別。
?4. 泛型不變性
?5. 泛型編譯時擦除
1. 為什么使用泛型
Java 為什么使用泛型-CSDN博客
2. 泛型的使用方式
2.1.?泛型類?
?泛型類是用類型參數定義類的一種方式。這些類型參數在聲明類變量或作為方法參數時會被具體的類型所替代。
public class Box<T> { private T t; public void set(T t) { this.t = t; } public T get() { return t; }
}
2.2.?泛型接口
泛型接口和泛型類的定義方式類似。
public interface List<E> extends Collection<E> { // ...
}
?2.3.?泛型方法
泛型方法是在方法定義中聲明類型參數的方法。
public static <T> T getFirst(List<T> list) { if (list == null || list.isEmpty()) { return null; } return list.get(0);
}
3. 泛型涉及的符號
3.1. 類型通配符"?"?
如 ?、? extends T、? super T。用于表示未知的類型,或表示某個類型的子類型或超類型。
- 無界通配符“?“:
無界通配符表示未知的類型。當使用無界通配符時,編寫時不能往這個通配符表示的集合中存放元素,但是可以從集合中獲取元素(并且只能賦值給?Object
?類型的變量或是進行類型轉換)。這是因為編譯器不知道集合中元素的具體類型,所以不能確保放入的元素與集合中已有的元素類型兼容。
但運行時可以賦值對象。
Class<?> clazz = Class.forName("com.mycompany.myreflect.Student");
- ? extends T:
表示未知的類型,但它是 T 或 T 的某個子類型。
我們就叫做上界限通配符,upper bounded wildcard。
當你需要讀取集合中的元素,并且你知道元素的類型至少是?T
?時,可以使用這種通配符。但是,你不能往這個集合中添加元素(除了?null
),因為編譯器無法確保你要添加的元素與集合中已有的元素類型兼容。
- ? super T:
表示未知的類型,但它是 T 或 T 的某個超類型。
?我們就叫做下界通配符, ???lower bounded wildcard。
當你需要向集合中添加元素,并且你知道這些元素的類型是?T
?或其子類時,可以使用這種通配符。同時,你也可以從集合中讀取元素,但是只能賦值給?Object
?類型的變量或是?T
?的超類型。
3.2. 占位符 T/K/V/E
T是占位符。其實它同K/V/E是一樣的沒有任何差別。只是我們的習慣會將它用在不同地方用于區別。
public class PrinterGen<T> {
//這個字符T,其實你可以使用你喜歡的字符代替,但是它必須和尖括號配合使用
//...
}
?3.3. 占位符T和通配符?的區別。
- 用途:泛型主要用于定義可重用的類、接口和方法,其中類型參數在編譯時確定。通配符主要用于表示對類型的約束或限制,通常用于泛型方法或泛型類的參數。
- 類型擦除:泛型在編譯時會被類型擦除,而通配符在運行時仍然存在,用于表示對類型的約束。
- 編譯時是否確定:如果編譯時可以確定類型的,就可以使用T。而一定要等到運行時才能確定具體類型的就需要使用?
而申明方法,類型,接口時,只能使用T,不能使用?。也是由于我們申明的方法等,在編譯時調用它的地方參數可以是不同的,但是必須是確定的。Class<?> clazz = Class.forName("com.mycompany.myreflect.Student");System.out.println(clazz);Class<Student> clazz = Student.class;System.out.println(clazz);
?4. 泛型不變性
Java中的泛型不變性(Generics Invariance)主要指的是泛型類型在編譯時的類型安全性質,它確保了泛型類型在聲明和使用時類型的一致性。?
?
這是因為雖然String是Object的子類,但是List<String>并不是List<Object>的子類。
?5. 泛型編譯時擦除
Java的泛型類型信息是在編譯時被擦除的,而不是在運行時。這是Java泛型實現的一個重要特性,稱為類型擦除(Type Erasure)。
在編譯時,Java編譯器會處理泛型代碼,生成不包含泛型類型信息的字節碼。具體來說,編譯器會將泛型類型參數替換為它們的上界(通常是Object
,除非明確指定了其他上界),并插入必要的類型轉換和類型檢查代碼以確保類型安全。這個過程被稱為類型擦除。
在運行時,Java虛擬機(JVM)加載并運行這些已經過類型擦除的字節碼。由于泛型類型信息已經被擦除,JVM不知道也不關心這些類型參數。它只看到普通的類和接口,以及普通的方法調用和字段訪問。
因此,雖然泛型提供了類型安全和更好的代碼可讀性,但它們并不會影響Java程序的運行時行為。泛型主要是一種編譯時的語法糖,用于提高代碼的可讀性和類型安全性,而不會增加任何運行時開銷。
源代碼
編譯后