【Java 泛型】泛型用法 ( 泛型编译期擦除 | 上界通配符 <? extends T> | 下界通配符 <? super T> )
文章目錄
- 一、泛型擦除
- 二、泛型的上界通配符 <? extends T>
- 三、泛型的下界通配符 <? super T>
一、泛型擦除
泛型只保留到 編譯期 , 在 編譯完畢后 , 泛型就不存在了 ;
在運(yùn)行時 , 通過反射 , 調(diào)用泛型類 , 即使違反了泛型規(guī)則 , 也能進(jìn)行相關(guān)操作 , 這是因?yàn)?在運(yùn)行時 , 已經(jīng)沒有泛型相關(guān)的限制 , 泛型限制在編譯時就已經(jīng)被擦除了 ;
但是 泛型的信息 , 保存在了常量表中 , 仍然可以獲取到 ;
泛型擦除 是為了 泛型可以兼容 老版本的 JDK 而設(shè)計(jì)的 ,
泛型是 JDK 5 加入的 , 如果添加了泛型 , 導(dǎo)致字節(jié)碼的格式改變 , 必然導(dǎo)致之前版本的 JDK 無法運(yùn)行有泛型的字節(jié)碼程序 ;
給定一個 Demo.java 類 , 其中定義了一個 <T> T get() 方法 ;
public class Demo {public <T> T get(){T t = null;return t;} }將其編譯后 , 查看字節(jié)碼附加信息 ;
D:\java>javap -v Demo.class Classfile /D:/java/Demo.classLast modified 2021-9-7; size 307 bytesMD5 checksum 727bc59421b23a5f0a31af0e91630ab8Compiled from "Demo.java" public class Demominor version: 0major version: 52flags: ACC_PUBLIC, ACC_SUPER Constant pool:#1 = Methodref #3.#14 // java/lang/Object."<init>":()V#2 = Class #15 // Demo#3 = Class #16 // java/lang/Object#4 = Utf8 <init>#5 = Utf8 ()V#6 = Utf8 Code#7 = Utf8 LineNumberTable#8 = Utf8 get#9 = Utf8 ()Ljava/lang/Object;#10 = Utf8 Signature#11 = Utf8 <T:Ljava/lang/Object;>()TT;#12 = Utf8 SourceFile#13 = Utf8 Demo.java#14 = NameAndType #4:#5 // "<init>":()V#15 = Utf8 Demo#16 = Utf8 java/lang/Object {public Demo();descriptor: ()Vflags: ACC_PUBLICCode:stack=1, locals=1, args_size=10: aload_01: invokespecial #1 // Method java/lang/Object."<init>":()V4: returnLineNumberTable:line 1: 0public <T extends java.lang.Object> T get();descriptor: ()Ljava/lang/Object;flags: ACC_PUBLICCode:stack=1, locals=2, args_size=10: aconst_null1: astore_12: aload_13: areturnLineNumberTable:line 3: 0line 4: 2Signature: #11 // <T:Ljava/lang/Object;>()TT; } SourceFile: "Demo.java"Demo 中的 get 方法類型返回值是 Ljava/lang/Object , 不是泛型 T , 這就是泛型在字節(jié)碼中被擦除了 ;
descriptor: ()Ljava/lang/Object;執(zhí)行下面的代碼 , 在運(yùn)行時 , 使用反射 , 向 list1 集合中添加 int 類型的元素 , 添加成功 ; 說明在運(yùn)行時 , 不再進(jìn)行泛型檢查 , 即使不符合泛型要求 , 也能操作成功 ;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List;public class Main {public static void main(String[] args) {// 編譯器 在 編譯期 進(jìn)行檢查List<String> list1 = new ArrayList<>();//list1.add(1);// 編譯器 在 編譯期 不進(jìn)行檢查List list2 = new ArrayList<String>();//list2.add(1);try {Method method = ArrayList.class.getMethod("add", Object.class);method.invoke(list1, 1);System.out.println("list1 集合大小 : " + list1.size());} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}} }執(zhí)行結(jié)果 :
二、泛型的上界通配符 <? extends T>
泛型 上界通配符 : <T extends Person> 表示泛型 T 是 Person 的子類 , <? extends T> 泛型類型 只能是 T 的子類 ;
- <T extends Person> 只能在聲明泛型時使用 , 不能在 使用 泛型 時使用 ;
- <? extends T> 只能在使用 泛型 時使用 , 不能在聲明 泛型類/方法 時 使用 ;
333 個有繼承關(guān)系的類 :
public class Animal { } public class Person extends Animal { } public class Woman extends Person { }泛型類 : 該泛型類接收一個泛型 , 該泛型 T 是 Person 的子類 ;
public class Data<T extends Person> { }main 函數(shù) :
// 上邊界通配符// 符合要求 , 可設(shè)置 Woman 的父類 , Woman 以及 Person 類Data<? super Woman> data4 = new Data<>();// 符合要求 , 可設(shè)置 Person 類Data<? super Person> data5 = new Data<>();// 不符合要求 , Animal 是最頂層的類 , 其取值都不符合 <T extends Person> 要求Data<? super Animal> data6 = new Data<>();報(bào)錯信息 : 傳入不符合要求的泛型的報(bào)錯信息 ;
三、泛型的下界通配符 <? super T>
泛型 下界通配符 : <? super T> 泛型類型 只能是 T 的父類 ;
- <? super T> 只能在使用 泛型 時使用 , 不能在聲明 泛型類/方法 時 使用 ;
333 個有繼承關(guān)系的類 :
public class Animal { } public class Person extends Animal { } public class Woman extends Person { }泛型類 : 該泛型類接收一個泛型 , 該泛型 T 是 Person 的子類 ;
public class Data<T extends Person> { }main 函數(shù) :
// 上邊界通配符// 符合要求 , 可設(shè)置 Woman 的父類 , Woman 以及 Person 類Data<? super Woman> data4 = new Data<>();// 符合要求 , 可設(shè)置 Person 類Data<? super Person> data5 = new Data<>();// 不符合要求 , Animal 是最頂層的類 , 其取值都不符合 <T extends Person> 要求//Data<? super Animal> data6 = new Data<>();報(bào)錯信息 : 不符合要求 , Animal 是最頂層的類 , 其取值都不符合 要求
總結(jié)
以上是生活随笔為你收集整理的【Java 泛型】泛型用法 ( 泛型编译期擦除 | 上界通配符 <? extends T> | 下界通配符 <? super T> )的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Java 泛型】泛型用法 ( 泛型类用
- 下一篇: 【Java 泛型】使用上下边界通配符解决