欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java 泛型總結(jié)(三):通配符的使用

 更新時(shí)間:2017年03月21日 11:02:38   作者:然則  
在泛型的使用中,還有個(gè)重要的東西叫通配符,本文介紹通配符的使用。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧

簡(jiǎn)介

前兩篇文章介紹了泛型的基本用法、類(lèi)型擦除以及泛型數(shù)組。在泛型的使用中,還有個(gè)重要的東西叫通配符,本文介紹通配符的使用。

這個(gè)系列的另外兩篇文章:

數(shù)組的協(xié)變

在了解通配符之前,先來(lái)了解一下數(shù)組。Java 中的數(shù)組是協(xié)變的,什么意思?看下面的例子:

class Fruit {}
class Apple extends Fruit {}
class Jonathan extends Apple {}
class Orange extends Fruit {}
public class CovariantArrays {
 public static void main(String[] args) { 
 Fruit[] fruit = new Apple[10];
 fruit[0] = new Apple(); // OK
 fruit[1] = new Jonathan(); // OK
 // Runtime type is Apple[], not Fruit[] or Orange[]:
 try {
  // Compiler allows you to add Fruit:
  fruit[0] = new Fruit(); // ArrayStoreException
 } catch(Exception e) { System.out.println(e); }
 try {
  // Compiler allows you to add Oranges:
  fruit[0] = new Orange(); // ArrayStoreException
 } catch(Exception e) { System.out.println(e); }
 }
} /* Output:
java.lang.ArrayStoreException: Fruit
java.lang.ArrayStoreException: Orange
*///:~

main 方法中的第一行,創(chuàng)建了一個(gè) Apple 數(shù)組并把它賦給 Fruit 數(shù)組的引用。這是有意義的,Apple 是 Fruit 的子類(lèi),一個(gè) Apple 對(duì)象也是一種 Fruit 對(duì)象,所以一個(gè) Apple 數(shù)組也是一種 Fruit 的數(shù)組。這稱(chēng)作數(shù)組的協(xié)變,Java 把數(shù)組設(shè)計(jì)為協(xié)變的,對(duì)此是有爭(zhēng)議的,有人認(rèn)為這是一種缺陷。

盡管 Apple[] 可以 “向上轉(zhuǎn)型” 為 Fruit[],但數(shù)組元素的實(shí)際類(lèi)型還是 Apple,我們只能向數(shù)組中放入 Apple或者 Apple 的子類(lèi)。在上面的代碼中,向數(shù)組中放入了 Fruit 對(duì)象和 Orange 對(duì)象。對(duì)于編譯器來(lái)說(shuō),這是可以通過(guò)編譯的,但是在運(yùn)行時(shí)期,JVM 能夠知道數(shù)組的實(shí)際類(lèi)型是 Apple[],所以當(dāng)其它對(duì)象加入數(shù)組的時(shí)候就會(huì)拋出異常。

泛型設(shè)計(jì)的目的之一是要使這種運(yùn)行時(shí)期的錯(cuò)誤在編譯期就能發(fā)現(xiàn),看看用泛型容器類(lèi)來(lái)代替數(shù)組會(huì)發(fā)生什么:

// Compile Error: incompatible types:
ArrayList<Fruit> flist = new ArrayList<Apple>();

上面的代碼根本就無(wú)法編譯。當(dāng)涉及到泛型時(shí), 盡管 Apple 是 Fruit 的子類(lèi)型,但是 ArrayList<Apple> 不是 ArrayList<Fruit> 的子類(lèi)型,泛型不支持協(xié)變。

使用通配符

從上面我們知道,List<Number> list = ArrayList<Integer> 這樣的語(yǔ)句是無(wú)法通過(guò)編譯的,盡管 Integer 是 Number 的子類(lèi)型。那么如果我們確實(shí)需要建立這種 “向上轉(zhuǎn)型” 的關(guān)系怎么辦呢?這就需要通配符來(lái)發(fā)揮作用了。

上邊界限定通配符

利用 <? extends Fruit> 形式的通配符,可以實(shí)現(xiàn)泛型的向上轉(zhuǎn)型:

public class GenericsAndCovariance {
 public static void main(String[] args) {
 // Wildcards allow covariance:
 List<? extends Fruit> flist = new ArrayList<Apple>();
 // Compile Error: can't add any type of object:
 // flist.add(new Apple());
 // flist.add(new Fruit());
 // flist.add(new Object());
 flist.add(null); // Legal but uninteresting
 // We know that it returns at least Fruit:
 Fruit f = flist.get(0);
 }
}

上面的例子中, flist 的類(lèi)型是 List<? extends Fruit>  我們可以把它讀作:一個(gè)類(lèi)型的 List, 這個(gè)類(lèi)型可以是繼承了 Fruit 的某種類(lèi)型。注意,這并不是說(shuō)這個(gè) List 可以持有 Fruit 的任意類(lèi)型。通配符代表了一種特定的類(lèi)型,它表示 “某種特定的類(lèi)型,但是 flist 沒(méi)有指定”。這樣不太好理解,具體針對(duì)這個(gè)例子解釋就是,flist 引用可以指向某個(gè)類(lèi)型的 List,只要這個(gè)類(lèi)型繼承自 Fruit,可以是 Fruit 或者 Apple,比如例子中的 new ArrayList<Apple> 但是為了向上轉(zhuǎn)型給 flist,flist 并不關(guān)心這個(gè)具體類(lèi)型是什么。

如上所述,通配符 List<? extends Fruit> 表示某種特定類(lèi)型 ( Fruit 或者其子類(lèi) ) 的 List,但是并不關(guān)心這個(gè)實(shí)際的類(lèi)型到底是什么,反正是 Fruit 的子類(lèi)型,F(xiàn)ruit 是它的上邊界。那么對(duì)這樣的一個(gè) List 我們能做什么呢?其實(shí)如果我們不知道這個(gè) List 到底持有什么類(lèi)型,怎么可能安全的添加一個(gè)對(duì)象呢?在上面的代碼中,向 flist 中添加任何對(duì)象,無(wú)論是 Apple 還是 Orange 甚至是 Fruit 對(duì)象,編譯器都不允許,唯一可以添加的是 null。所以如果做了泛型的向上轉(zhuǎn)型 (List<? extends Fruit> flist = new ArrayList<Apple>()),那么我們也就失去了向這個(gè) List 添加任何對(duì)象的能力,即使是 Object 也不行。

另一方面,如果調(diào)用某個(gè)返回 Fruit 的方法,這是安全的。因?yàn)槲覀冎?,在這個(gè) List 中,不管它實(shí)際的類(lèi)型到底是什么,但肯定能轉(zhuǎn)型為 Fruit,所以編譯器允許返回 Fruit。

了解了通配符的作用和限制后,好像任何接受參數(shù)的方法我們都不能調(diào)用了。其實(shí)倒也不是,看下面的例子:

public class CompilerIntelligence {
 public static void main(String[] args) {
 List<? extends Fruit> flist =
 Arrays.asList(new Apple());
 Apple a = (Apple)flist.get(0); // No warning
 flist.contains(new Apple()); // Argument is ‘Object'
 flist.indexOf(new Apple()); // Argument is ‘Object'
 //flist.add(new Apple()); 無(wú)法編譯
 }
}

在上面的例子中,flist 的類(lèi)型是 List<? extends Fruit> ,泛型參數(shù)使用了受限制的通配符,所以我們失去了向其中加入任何類(lèi)型對(duì)象的例子,最后一行代碼無(wú)法編譯。

但是 flist 卻可以調(diào)用 contains 和 indexOf 方法,它們都接受了一個(gè) Apple 對(duì)象做參數(shù)。如果查看 ArrayList 的源代碼,可以發(fā)現(xiàn) add() 接受一個(gè)泛型類(lèi)型作為參數(shù),但是 contains 和 indexOf 接受一個(gè) Object 類(lèi)型的參數(shù),下面是它們的方法簽名:

public boolean add(E e)
public boolean contains(Object o)
public int indexOf(Object o)

所以如果我們指定泛型參數(shù)為 <? extends Fruit> 時(shí),add() 方法的參數(shù)變?yōu)?? extends Fruit,編譯器無(wú)法判斷這個(gè)參數(shù)接受的到底是 Fruit 的哪種類(lèi)型,所以它不會(huì)接受任何類(lèi)型。

然而,contains 和 indexOf 的類(lèi)型是 Object,并沒(méi)有涉及到通配符,所以編譯器允許調(diào)用這兩個(gè)方法。這意味著一切取決于泛型類(lèi)的編寫(xiě)者來(lái)決定那些調(diào)用是 “安全” 的,并且用 Object 作為這些安全方法的參數(shù)。如果某些方法不允許類(lèi)型參數(shù)是通配符時(shí)的調(diào)用,這些方法的參數(shù)應(yīng)該用類(lèi)型參數(shù),比如 add(E e)。

當(dāng)我們自己編寫(xiě)泛型類(lèi)時(shí),上面介紹的就有用了。下面編寫(xiě)一個(gè) Holder 類(lèi):

public class Holder<T> {
 private T value;
 public Holder() {}
 public Holder(T val) { value = val; }
 public void set(T val) { value = val; }
 public T get() { return value; }
 public boolean equals(Object obj) {
 return value.equals(obj);
 }
 public static void main(String[] args) {
 Holder<Apple> Apple = new Holder<Apple>(new Apple());
 Apple d = Apple.get();
 Apple.set(d);
 // Holder<Fruit> Fruit = Apple; // Cannot upcast
 Holder<? extends Fruit> fruit = Apple; // OK
 Fruit p = fruit.get();
 d = (Apple)fruit.get(); // Returns ‘Object'
 try {
  Orange c = (Orange)fruit.get(); // No warning
 } catch(Exception e) { System.out.println(e); }
 // fruit.set(new Apple()); // Cannot call set()
 // fruit.set(new Fruit()); // Cannot call set()
 System.out.println(fruit.equals(d)); // OK
 }
} /* Output: (Sample)
java.lang.ClassCastException: Apple cannot be cast to Orange
true
*///:~

在 Holer 類(lèi)中,set() 方法接受類(lèi)型參數(shù) T 的對(duì)象作為參數(shù),get() 返回一個(gè) T 類(lèi)型,而 equals() 接受一個(gè) Object 作為參數(shù)。fruit 的類(lèi)型是 Holder<? extends Fruit>,所以set()方法不會(huì)接受任何對(duì)象的添加,但是 equals() 可以正常工作。

下邊界限定通配符

通配符的另一個(gè)方向是 “超類(lèi)型的通配符“: ? super TT是類(lèi)型參數(shù)的下界。使用這種形式的通配符,我們就可以 ”傳遞對(duì)象” 了。還是用例子解釋?zhuān)?/p>

public class SuperTypeWildcards {
 static void writeTo(List<? super Apple> apples) {
 apples.add(new Apple());
 apples.add(new Jonathan());
 // apples.add(new Fruit()); // Error
 }
}

writeTo 方法的參數(shù) apples 的類(lèi)型是 List<? super Apple>  它表示某種類(lèi)型的 List,這個(gè)類(lèi)型是 Apple 的基類(lèi)型。也就是說(shuō),我們不知道實(shí)際類(lèi)型是什么,但是這個(gè)類(lèi)型肯定是 Apple 的父類(lèi)型。因此,我們可以知道向這個(gè) List 添加一個(gè) Apple 或者其子類(lèi)型的對(duì)象是安全的,這些對(duì)象都可以向上轉(zhuǎn)型為 Apple。但是我們不知道加入 Fruit 對(duì)象是否安全,因?yàn)槟菢訒?huì)使得這個(gè) List 添加跟 Apple 無(wú)關(guān)的類(lèi)型。

在了解了子類(lèi)型邊界和超類(lèi)型邊界之后,我們就可以知道如何向泛型類(lèi)型中 “寫(xiě)入” ( 傳遞對(duì)象給方法參數(shù)) 以及如何從泛型類(lèi)型中 “讀取” ( 從方法中返回對(duì)象 )。下面是一個(gè)例子:

public class Collections { 
 public static <T> void copy(List<? super T> dest, List<? extends T> src) 
 {
 for (int i=0; i<src.size(); i++) 
 dest.set(i,src.get(i)); 
 } 
}

src 是原始數(shù)據(jù)的 List,因?yàn)橐獜倪@里面讀取數(shù)據(jù),所以用了上邊界限定通配符:<? extends T>,取出的元素轉(zhuǎn)型為 T。dest 是要寫(xiě)入的目標(biāo) List,所以用了下邊界限定通配符:<? super T>,可以寫(xiě)入的元素類(lèi)型是 T 及其子類(lèi)型。

無(wú)邊界通配符

還有一種通配符是無(wú)邊界通配符,它的使用形式是一個(gè)單獨(dú)的問(wèn)號(hào):List<?>,也就是沒(méi)有任何限定。不做任何限制,跟不用類(lèi)型參數(shù)的 List 有什么區(qū)別呢?

List<?> list表示 list 是持有某種特定類(lèi)型的 List,但是不知道具體是哪種類(lèi)型。那么我們可以向其中添加對(duì)象嗎?當(dāng)然不可以,因?yàn)椴⒉恢缹?shí)際是哪種類(lèi)型,所以不能添加任何類(lèi)型,這是不安全的。而單獨(dú)的 List list ,也就是沒(méi)有傳入泛型參數(shù),表示這個(gè) list 持有的元素的類(lèi)型是 Object,因此可以添加任何類(lèi)型的對(duì)象,只不過(guò)編譯器會(huì)有警告信息。

總結(jié)

通配符的使用可以對(duì)泛型參數(shù)做出某些限制,使代碼更安全,對(duì)于上邊界和下邊界限定的通配符總結(jié)如下:

  • 使用 List<? extends C> list 這種形式,表示 list 可以引用一個(gè) ArrayList ( 或者其它 List 的 子類(lèi) ) 的對(duì)象,這個(gè)對(duì)象包含的元素類(lèi)型是 C 的子類(lèi)型 ( 包含 C 本身)的一種。
  • 使用 List<? super C> list 這種形式,表示 list 可以引用一個(gè) ArrayList ( 或者其它 List 的 子類(lèi) ) 的對(duì)象,這個(gè)對(duì)象包含的元素就類(lèi)型是 C 的超類(lèi)型 ( 包含 C 本身 ) 的一種。

大多數(shù)情況下泛型的使用比較簡(jiǎn)單,但是如果自己編寫(xiě)支持泛型的代碼需要對(duì)泛型有深入的了解。這幾篇文章介紹了泛型的基本用法、類(lèi)型擦除、泛型數(shù)組以及通配符的使用,涵蓋了最常用的要點(diǎn),泛型的總結(jié)就寫(xiě)到這里。

以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!

相關(guān)文章

  • Java中實(shí)現(xiàn)Comparator接口和用法實(shí)例(簡(jiǎn)明易懂)

    Java中實(shí)現(xiàn)Comparator接口和用法實(shí)例(簡(jiǎn)明易懂)

    這篇文章主要介紹了Java中實(shí)現(xiàn)Comparator接口和用法實(shí)例(簡(jiǎn)明易懂),本文給出實(shí)現(xiàn)Comparator接口的實(shí)例和使用這個(gè)接口的代碼實(shí)例,需要的朋友可以參考下
    2015-05-05
  • Javaweb EL自定義函數(shù)開(kāi)發(fā)及代碼實(shí)例

    Javaweb EL自定義函數(shù)開(kāi)發(fā)及代碼實(shí)例

    這篇文章主要介紹了Javaweb EL自定義函數(shù)開(kāi)發(fā)及代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06
  • SpringBoot用多線程批量導(dǎo)入數(shù)據(jù)庫(kù)實(shí)現(xiàn)方法

    SpringBoot用多線程批量導(dǎo)入數(shù)據(jù)庫(kù)實(shí)現(xiàn)方法

    這篇文章主要介紹了SpringBoot用多線程批量導(dǎo)入數(shù)據(jù)庫(kù)實(shí)現(xiàn)方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2023-02-02
  • Spring boot項(xiàng)目整合WebSocket方法

    Spring boot項(xiàng)目整合WebSocket方法

    這篇文章主要介紹了WebSocket使用Spring boot整合方法,需要繼承webSocketHandler類(lèi),重寫(xiě)幾個(gè)方法就可以了,具體實(shí)例代碼跟隨小編一起看看吧
    2021-09-09
  • 三種簡(jiǎn)單排序算法(使用java實(shí)現(xiàn))

    三種簡(jiǎn)單排序算法(使用java實(shí)現(xiàn))

    下面小編就為大家?guī)?lái)一篇三種簡(jiǎn)單排序算法(使用java實(shí)現(xiàn))。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2016-07-07
  • SpringBoot JWT實(shí)現(xiàn)token登錄刷新功能

    SpringBoot JWT實(shí)現(xiàn)token登錄刷新功能

    JWT本身是無(wú)狀態(tài)的,這點(diǎn)有別于傳統(tǒng)的session,不在服務(wù)端存儲(chǔ)憑證。這種特性使其在分布式場(chǎng)景,更便于擴(kuò)展使用。接下來(lái)通過(guò)本文給大家分享SpringBoot JWT實(shí)現(xiàn)token登錄刷新功能,感興趣的朋友一起看看吧
    2021-09-09
  • 一步步教你整合SSM框架(Spring MVC+Spring+MyBatis)詳細(xì)教程

    一步步教你整合SSM框架(Spring MVC+Spring+MyBatis)詳細(xì)教程

    使用SSM(Spring、SpringMVC和Mybatis)已經(jīng)有段時(shí)間了,項(xiàng)目在技術(shù)上已經(jīng)沒(méi)有什么難點(diǎn)了,基于現(xiàn)有的技術(shù)就可以實(shí)現(xiàn)想要的功能,下面這篇文章主要給大家介紹了關(guān)于整合SSM框架:Spring MVC + Spring + MyBatis的相關(guān)資料,需要的朋友可以參考下。
    2017-07-07
  • java遍歷properties文件操作指南

    java遍歷properties文件操作指南

    在java項(xiàng)目開(kāi)發(fā)過(guò)程中,使用properties文件作為配置基本上是必不可少的,有很多如系統(tǒng)配置信息,java如何遍歷properties文件呢,本文將詳細(xì)介紹,希望可以幫助到您
    2012-11-11
  • Java中的@RequiredArgsConstructor注解詳解

    Java中的@RequiredArgsConstructor注解詳解

    這篇文章主要介紹了Java中的@RequiredArgsConstructor注解詳解,@RequiredArgsConstructor是Lombok的一個(gè)注解,簡(jiǎn)化了我們對(duì)@Autowired書(shū)寫(xiě),@RequiredArgsConstructor注解可以代替@Autowired注解,需要的朋友可以參考下
    2024-01-01
  • Mybatis之動(dòng)態(tài)sql標(biāo)簽的使用

    Mybatis之動(dòng)態(tài)sql標(biāo)簽的使用

    這篇文章主要介紹了Mybatis之動(dòng)態(tài)sql標(biāo)簽的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-06-06

最新評(píng)論