国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

泛型之通配符

PingCAP / 2365人閱讀

摘要:調用相當于通過使用通配符,可以傳遞任何類型的對象,但也是有缺點的。使用通配符,賦值傳值的時候方便了,但是對泛型類中參數為泛型的方法起到了副作用。結論當使用父界限定通配符時,泛型類中返回值為泛型的方法不能使用。

數組 VS List

第一回合
數組類型為Object,可以存儲任意類型的對象,List集合同樣可以做到

Object[] obj = new Object[1];
List list = new ArrayList();

第二回合
數組類型可以為Integer類型,專門存儲Integer類型對象,List集合同樣也可以

Integer[] integer = new Integer[1];
List list = new ArrayList<>();

決勝局
前兩局雙方打成平手,最后的決勝局。先看數組代碼:

Object[] obj = new Integer[2];
obj[0] = 52021;
//編譯期OK,運行期就掛了
obj[1] = "Full of confidence";

上面的代碼在運行期會拋出異常:java.lang.ArrayStoreException: java.lang.String

List集合代碼:

//編譯期報錯
List obj = new ArrayList();

最終結果,List集合更勝一籌,因為它能盡早的發現錯誤。

分析最后一戰中數組的代碼:

在編譯期間,編譯器不會檢查這樣的賦值,編譯器覺得它是合理的,因為父類引用指向子類對象,沒有任何問題。賦值時,將字符串存儲在一個Object類型的數組中也說的過去,但在運行時,發現明明內存分配的是存儲整型類型對象的格子,里面卻放著字符串類型的對象,自然會報錯了。

分析最后一戰中List集合的代碼:

由于運用了泛型,在編譯期就發現了錯誤,避免了將問題帶到運行時。
思考個問題,如果代碼在編譯期沒有報錯會發生什么?

在編譯期沒有報錯并且在運行期會將泛型擦除,全部變為了Object類型。所以執行obj.add(new Integer(1))也是可以的。如果真是這樣的話,那么泛型還有什么存在的意義呢?所以這種假設是不存在的,所以會在編譯期報錯。編譯期報錯的原因就是在使用泛型時,泛型的引用和創建兩端,給出的泛型變量不相同,所以在使用泛型時,泛型的引用和創建兩端,給出的泛型變量必須相同。

通配符

通配符只能出現在等號左面,不能出現在new的一邊。

List list = new ArrayList()

List obj1 = new ArrayList()

List obj = new ArrayList()

無界通配符
public void foo() {
    List list = new ArrayList();
    //foo2(list); //編譯期報錯
    foo3(list); //正常編譯
}

public void foo2(List list) {//TODO}
public void foo3(List list){//TODO}

上面代碼中,調用foo2方法編譯期報錯原因:泛型的引用和創建兩端,給出的泛型變量不一致,相當于:List list = new ArrayList();

public void foo() {
    List list = new ArrayList();
    List strList = new ArrayList();
    foo3(list); //正常編譯
}
public void foo3(List list) {
    //TODO
}

現在希望將strList作為參數調用foo3方法,這時就想到了方法的重載,所以定義了一個重載的方法。public void foo3(List list){//TODO}
定義完成后,竟然報錯了,并且foo3(List list)也報錯了。這是由于泛型擦除導致的。在運行期,會有泛型擦除,所以foo3(List list)foo3(List list)會變成一樣的方法,所以在編譯期就要報錯,否則在運行期就無法區分了。

這里無法使用foo3方法重載,除非定義不同名字的方法。除了定義不同名字的方法之外,還可以使用通配符。

public void foo() {
    List list = new ArrayList();
    List strList = new ArrayList();
    foo3(list);
    foo3(strList);
}
public void foo3(List list) {
    //TODO
}

調用foo3(strList);相當于:
List list = new ArrayList(); => List list = strList

通過使用通配符,foo3(List list);可以傳遞任何類型的對象,但也是有缺點的。使用通配符,賦值/傳值的時候方便了,但是對泛型類中參數為泛型的方法起到了副作用。如何理解泛型類中參數為泛型的方法起到了副作用這句話呢?結合代碼來看

/**
 * 使用 ? 通配符
 */
public void foo3(List list) {
    //list.add(1); //編譯期報錯,起到了副作用
    //list.add("hello"); //編譯期報錯,起到了副作用
    Object o = list.get(0); //其實也是作廢的,只是由于Object是所有類的父類,所以這里不會報錯。
}

List定義:接口ListE代表是泛型類;List中add方法定義:boolean add(E e),參數為E,說明參數為泛型。List接口使用通配符,調用add方法時,副作用是在編譯期報錯;泛型類中返回值為泛型的方法,也作廢了,如:get方法定義:E get(int index)

子界限定

子界限定:? extends Number

解釋:? 是 Number 類型或者是 Number 的子類型

缺點:參數為泛型的方法不能使用

public void foo4() {
    List intList = new ArrayList();
    List longList = new ArrayList();
    foo5(intList); //Integer是Number的子類型
    foo5(longList); //Long是Number的子類型
}
public void foo5(List list) {
    //list.add(1); //編譯期報錯
    //list.add(2L); //編譯期報錯
}

分析以上代碼:

foo5(intList);相當于List list = intList;賦值操作,這是沒有問題的;list.add(1);則會在編譯期報錯。
list定義的類型是List,它帶有泛型,而add方法的參數也是泛型類型,符合:泛型類中參數為泛型的方法起到了副作用這個結論。所以調用add編譯期報錯。

想想看,如果list.add(1);不報錯:

foo4中調用了foo5(longList);相當于List list = new ArrayList();,然后執行foo5,調用list.add(1);如果不報錯也就相當于Long類型容器可以盛放Integer類型數據,這樣一來,泛型也就沒有意義了。有人也許會問,既然add方法會報錯,為什么foo5(longList);沒有問題?其實我覺得這是不一樣的,調用add方法不確定因素很多,因為類型可能是Integer,也可能是Long,人們無法保證在調用add方法時,只傳遞相同類型的變量,所以程序就直接限定死了,你不可以add任何東西。但直接賦值,這個值是可以確定的,類型具有統一性,要是什么都是什么,所以是可行的。

結論:當使用子界限定通配符時,泛型類中參數為泛型的方法不能使用

也許有人會問,這樣做是否可以確定類型?

List list = new ArrayList();
list.add(1); //編譯期報錯

很遺憾,這樣也是不行的。? 仍然代表了不確定性,所以編譯器壓根就是將這種方式的方法的參數類型是泛型的全部廢掉了。

add不能用,那賦值操作后可從中取值嗎?答案是肯定的。
Number number = list.get(0);

分析以上代碼:

無論返回什么值,總歸都是Number類型的,這是可以確定的,所以可以用Number接收返回值為泛型的方法。當然,這里說沒有問題也只能是Number類型或者是Object類型接收,別的類型是不可以的。

結論:當使用子界限定通配符時,泛型類中返回值為泛型的方法可以使用

父界限定

父界限定:? super Integer

解釋:? 是Integer類型或者是Integer的父類型

缺點:返回值為泛型的方法不能使用

public void foo6() {
    List numList = new ArrayList();
    List intList = new ArrayList();
    foo7(numList); //Number是Integer的父類型
    foo7(intList); //Integer是本身
}
public void foo7(List list) {
    list.add(1);
}

分析以上代碼:

list.add(1);不會報錯,因為類型可以確定。

如果List list的賦值是List list = new ArrayList();沒問題

如果List list的賦值是List list = new ArrayList();沒問題

如果List list的賦值是List list = new ArrayList();也沒問題

所以只要add(1)就肯定沒有問題,就是說add(1),這個實參1,符合任何一種情況。

結論:當使用父界限定通配符時,泛型類中參數為泛型的方法可以使用

public void foo7(List list) {
    list.add(1);
    Object obj = list.get(0);
}

Object obj = list.get(0);這句話沒有報錯,但其實是作廢的,只是由于Object是所有類的父類,所以才可以這么用。

結論:當使用父界限定通配符時,泛型類中返回值為泛型的方法不能使用

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73385.html

相關文章

  • Java 型之上界下界配符

    摘要:泛型之上界下界通配符本教程是為編寫的。這是在使用泛型編程時一個常見的誤解,也是一個需要學習的重要概念。通配符使用指南學習使用泛型編程時,更令人困惑的一個方面是確定何時使用上限有界通配符以及何時使用下限有界通配符。 Java 泛型之上界下界通配符 本Java教程是為JDK 8編寫的。本頁描述的示例和實踐沒有利用后續版本中引入的改進。 泛型,繼承和子類 如你所知,只要類型兼容,就可以將一種...

    shiyang6017 評論0 收藏0
  • 型之泛型

    摘要:定義具有一個或多個類型變量的類,稱之為泛型類。泛型類的繼承創建對象的兩種方式錯誤方式錯誤原因繼承了泛型類,但并不是泛型類,所以不能這樣創建對象。同樣是泛型類,它的父類也是泛型類,它傳遞的是常量。 泛型類 public class A { //在成員變量上使用泛型 private T t; public A() {} //構造參數類型上...

    caoym 評論0 收藏0
  • 型之泛型方法

    摘要:泛型方法顯式賦值張三李四王五隱式賦值,常用此方式,可以不指定張三李四王五泛型方法不受類的限制,也就是說,即使方法所在的類不是泛型類,也可以定義泛型方法在泛型類中定義的方法,也不一定是泛型方法,就看你如何定義了。泛型類中可以定義泛型方法。 public class F { //泛型方法 public static T getT(T[] array...

    sydMobile 評論0 收藏0
  • 淺析Java泛型

    摘要:泛型類在類的申明時指定參數,即構成了泛型類。換句話說,泛型類可以看成普通類的工廠。的作用就是指明泛型的具體類型,而類型的變量,可以用來創建泛型類的對象。只有聲明了的方法才是泛型方法,泛型類中的使用了泛型的成員方法并不是泛型方法。 什么是泛型? 泛型是JDK 1.5的一項新特性,它的本質是參數化類型(Parameterized Type)的應用,也就是說所操作的數據類型被指定為一個參數,...

    godiscoder 評論0 收藏0
  • JAVA泛型筆記

    摘要:泛型類泛型類和普通類的區別就是類定義時,在類名后加上泛型聲明。泛型類的內部成員方法就可以使用聲明的參數類型。 泛型是JDK 1.5的一項新特性,它的本質是參數化類型(Parameterized Type),即所操作的數據類型在定義時被指定為一個參數。當我們使用的時候給這個參數指定不同的對象類型,就可以處理不同的對象。這種參數類型可以用在類、接口和方法的創建中,分別稱為泛型類、泛型接口和...

    n7then 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<