日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

你是否真正理解了泛型、通配符、类型擦除

發布時間:2024/10/5 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 你是否真正理解了泛型、通配符、类型擦除 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

泛型結🐕

  • 前言
  • 一、使用反射繞過編譯時的類型檢查
  • 二、為什么要引入泛型的呢?
  • 三、泛型的使用方式
    • 1.泛型類
    • 2.泛型方法
    • 3.泛型接口
  • 四、通配符的使用
    • 1.通配符上限<? extends T>
    • 2.下界通配符 < ? super E>
    • 3. T 和 ? 的區別
  • 五、類型擦除


你是否還不知道為什么要引入泛型?
你是否還不知道怎么使用泛型?
你是否還不知道類型擦除?
你是否還不知道怎么使用通配符?

那么讀上幾分鐘,讓你的人生不再遺憾?哈撒給😁


前言

泛型是JDK1.5引入的新特性,泛型提供了編譯時類型檢測安全機制。

可能不是太好理解,下面使用代碼演示


沒錯,當你定義了泛型為Integer類型之后,試圖向集合中加入String類型,那編譯肯定不會通過。

至于怎么向Integer集合類型中加入String類型,那么就肯定要繞過編譯了!
然后你就可以想到使用什么了,沒錯,就是反射!


一、使用反射繞過編譯時的類型檢查

List<Integer> list = new ArrayList<>();list.add(2);String str = "a"; // list.add(str);//實例化對象Class<? extends List> clazz = list.getClass();//獲取add類,因為反射會將類的屬性、構造函數、方法等成為一個個的對象Method add = clazz.getDeclaredMethod("add", Object.class);//執行invoke方法,向集合中添加數據add.invoke(list,str);System.out.println(list);


至于原理我已經在反射一文講清楚了。該文是講泛型的,反射就不多bb了。


二、為什么要引入泛型的呢?

在JDK1.5之前,如果想要實現一個通用的方法,那么就需要使用Object

/*** @author:抱著魚睡覺的喵喵* @date:2021/4/1* @description:*/ public class Common {private Object[] data;public Common(int length) {this.data = new Object[length];}public Object getData(int index) {return data[index];}public void addData(int index, Object data) {this.data[index] = data;} }

然后就使用唄

Common common = new Common(5);common.addData(0,"zsh");common.addData(1,23);String data = (String) common.getData(0);String data2 = (String) common.getData(1);System.out.println(data);System.out.println(data2);


從這可以看出使用Object可以實現方法的通用,但是有個很大的弊端

當我向數組中加入整型和字符串時,在獲取數據時,轉換為String類型失敗,當然都能明白這個應該轉換為Integer類型

但是如果數據量較大時,是不是很難避免類型轉換異常

所以為了代碼得到重(chong)用等等一系列的原因,泛型便出現了


三、泛型的使用方式

1.泛型類

public class Generic<T> {private T data;public T getData() {return data;}public void setData(T data) {this.data = data;} } public class GenericExtends<T> extends Generic<T> {} class GenericExtends2<T> extends Generic<String> {}

2.泛型方法

public class Generic<T> {public <E> void iterator(E[] arr) {for (E ele : arr) {System.out.printf("%s\t",ele);}} } Generic generic = new Generic();Integer arr[] = {1,35,56};String[] str ={"hello"};generic.iterator(arr);generic.iterator(str);

3.泛型接口

public interface Alorgithm<T>{T method(); }

四、通配符的使用

我們經常會遇見這幾種通配符,如T E K V ?,那么它們都是什么意思呢?

其實這幾個字符是約定俗成的,你可以使用A-Z的任何一個字符,但這樣的話,不太容易理解語義。

T:表示一個Java類型
?: 表示不確定的 java 類型
K V : java鍵值中的Key Value
E :代表Element

或許不是很好理解,那么看完下面的例子你就明白了

1.通配符上限<? extends T>

現在我自定義四個類,分別是Car、BusCar、SportCar、Airport
其中BusCar和SportCar繼承自Car,Airport與其它幾個類無任何關系

public class Car {public String toString() {return "car ---> run build";} } public class BusCar extends Car{@Overridepublic String toString() {return "busCar ----> run build";} } public class SportCar extends Car{@Overridepublic String toString() {return "sportCar -----> run build";} } public class Airport {public String toString() {return "airPort ---> run build";} }

現在我要對Car類型的子類型車做出統一操作

public static void start(ArrayList<Car> cars) {for (Object car : cars) {System.out.println(car);}}public static void main(String[] args) {ArrayList<BusCar> list = new ArrayList<>();BusCar busCar = new BusCar();BusCar busCar2 = new BusCar();list.add(busCar);list.add(busCar2);start(list);}


發現ArrayList<BusCar>向ArrayList< Car>類型轉換失敗了,那這是為什么呢? 我們都知道BusCar是直接可以轉換為Car類的;子類->父類

其實這也很簡單,ArrayList<BusCar>和ArrayList< Car>是不同的類型,我們都知道泛型都有安全類型檢測;假如說上面的可以轉換,那豈不是我也可以向其中添加SportsCar了,甚至Car本身都能添加,這樣就亂套了。(本來是一群BusCar,突然來了一個SportsCar,這個SportsCar還不被群毆死呀)


重點來了

所以可以使用?

再次運行

這樣以來也可以保證類型的安全,如果在這種情況下加入其他類型的就會報錯。

雖然可以這樣使用,但是如果我直接向其中只加入Airport類會怎么樣呢(Airport不是Car的子類)

這樣以來是不是有很大的問題,本來start方法只是用來只執行Car類或者Car子類的;突然跑來一個無關的Airport(飛機)類,而且還執行成功了,是不是很荒唐,所以通配符上限就來了。

<? extends T> 表名該類型必須是T類或者T的子類,T就代表向上的極限值。

如果再使用與Car無關的類,就會報錯;



2.下界通配符 < ? super E>

表示該類型必須是E本身或者E的父類(超類),上限是Object

3. T 和 ? 的區別


1.有時候我們可以對 T 進行操作,但是對 ?卻 不行,比如如下這種

T car = start(); //正確? car = start(); //錯誤

T表示一個Java類型
?表示一個不確定的Java類型

2.T可以多重限定而?不行

這個T必須是Auth和Alorgithm的共同子類;?不可以的原因是?表示的是一個不確定的類型;

public class Alu implements Alorgithm,Auth{ //這個T必須是Auth和Alorgithm的共同子類private static<T extends Auth & Alorgithm> void test(T t) {} //接口方法@Overridepublic Object method() {return null;} }

3.通配符?可以使用超類限定而T不可以

T extends A

? extends A
? super A


五、類型擦除

List<Integer> list = new ArrayList<>();list.add(2);String str = "a";list.add(str); //報錯System.out.println(list);

這樣添加一個整型之后,再添加一個字符串,肯定報錯;因為泛型在編譯期間會進行類型安全檢測

但是下面這樣就可以

List<Integer> list = new ArrayList<>();list.add(2);String str = "a";//實例化對象Class<? extends List> clazz = list.getClass();//獲取add對象,因為反射會把屬性,構造函數,方法等分為一個個的對象Method add = clazz.getDeclaredMethod("add", Object.class);//執行invoke操作,也就是添加str到list中add.invoke(list,str);System.out.println(list);

使用反射繞過了編譯,其實Java的泛型是偽泛型,意思是在編譯期間,所有的泛型信息都會被擦除;比如上面我定義的泛型是List< Integer >類型,但是到了編譯過后就變成了List,JVM只看到了List,看不到任何泛型的信息;

所以在編譯器要盡可能的發現錯誤;但仍無法避免類型轉換異常

總結

以上是生活随笔為你收集整理的你是否真正理解了泛型、通配符、类型擦除的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。