最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

最新Java?泛型中的通配符講解

 更新時(shí)間:2022年06月22日 14:15:12   作者:雨點(diǎn)的名字  
Java的泛型是偽泛型,那是因?yàn)榉盒托畔⒅淮嬖谟诖a編譯階段,在生成的字節(jié)碼中是不包含泛型中的類型信息的,使用泛型的時(shí)候加上類型參數(shù),在編譯器編譯的時(shí)候會(huì)去掉,這個(gè)過程為類型擦除,這篇文章主要介紹了Java?泛型中的通配符,需要的朋友可以參考下

本文內(nèi)容如下:

1、 什么是類型擦除
2、常用的 ?, T, E, K, V, N的含義
3、上界通配符 < ?extends E>
4、下界通配符 < ?super E>
5、什么是PECS原則
6、通過一個(gè)案例來(lái)理解 ?和 T 和 Object 的區(qū)別

一、什么是類型擦除?

我們說(shuō)Java的泛型是偽泛型,那是因?yàn)榉盒托畔⒅淮嬖谟诖a編譯階段,在生成的字節(jié)碼中是不包含泛型中的類型信息的,使用泛型的時(shí)候加上類型參數(shù),在編譯器編譯的時(shí)候會(huì)去掉,這個(gè)過程為類型擦除。

泛型是Java 1.5版本才引進(jìn)的概念,在這之前是沒有泛型的,但是因?yàn)轭愋筒脸匦裕尫盒痛a能夠很好地和之前版本的代碼兼容。

我們來(lái)看個(gè)案例

(圖1)

因?yàn)檫@里泛型定義為Integer類型集合,所以添加String的時(shí)候在編譯時(shí)期就會(huì)直接報(bào)錯(cuò)。

那是不是就一定不能添加了呢?答案是否定的,我們可以通過Java泛型中的類型擦除特點(diǎn)及反射機(jī)制實(shí)現(xiàn)。

如下

  public static void main(String[] args) throws Exception {
        ArrayList<Integer> list = new ArrayList();
        list.add(6);
        //反射機(jī)制實(shí)現(xiàn)
        Class<? extends ArrayList> clazz = list.getClass();
        Method add = clazz.getDeclaredMethod("add", Object.class);
        add.invoke(list, "歡迎關(guān)注:后端元宇宙");
        System.out.println("list = " + list);
    }

運(yùn)行結(jié)果

list = [6, 歡迎關(guān)注:后端元宇宙]

二、案例實(shí)體準(zhǔn)備

這里先建幾個(gè)實(shí)體,為后面舉例用

Animal類

@Data
@AllArgsConstructor
public class Animal {

    /**
     * 動(dòng)物名稱
     */
    private String name;

    /**
     * 動(dòng)物毛色
     */
    private String color;
}

Pig類 :Pig是Animal的子類

public class Pig  extends  Animal{
    public Pig(String name,String color){
        super(name,color);
    }
}

Dog類: Dog也是Animal的子類

public class Dog extends Animal {

    public Dog(String name,String color){
        super(name,color);
    }
}

三、常用的 ?, T, E, K, V, N的含義

我們?cè)诜盒椭惺褂猛ㄅ浞?jīng)??吹絋、F、U、E,K,V其實(shí)這些并沒有啥區(qū)別,我們可以選 A-Z 之間的任何一個(gè)字母都可以,并不會(huì)影響程序的正常運(yùn)行。

只不過大家心照不宣的在命名上有些約定:

  • T (Type) 具體的Java類
  • E (Element)在集合中使用,因?yàn)榧现写娣诺氖窃?/li>
  • K V (key value) 分別代表java鍵值中的Key Value
  • N (Number)數(shù)值類型
  • ? 表示不確定的 Java 類型

四、上界通配符 < ? extends E>

語(yǔ)法:<? extends E>

舉例:<? extends Animal> 可以傳入的實(shí)參類型是Animal或者Animal的子類

兩大原則

  • add:除了null之外,不允許加入任何元素!
  • get:可以獲取元素,可以通過E或者Object接受元素!因?yàn)椴还艽嫒胧裁磾?shù)據(jù)類型都是E的子類型

示例

  public static void method(List<? extends Animal> lists){
        //正確 因?yàn)閭魅氲囊欢ㄊ茿nimal的子類
        Animal animal = lists.get(0);
        //正確 當(dāng)然也可以用Object類接收,因?yàn)镺bject是頂層父類
        Object object = lists.get(1);
        //錯(cuò)誤 不能用?接收
        ? t = lists.get(2);
        // 錯(cuò)誤
        lists.add(new Animal());
        //錯(cuò)誤
        lists.add(new Dog());
        //錯(cuò)誤 
        lists.add(object);
        //正確 除了null之外,不允許加入任何元素!
        lists.add(null);
    }

五、下界通配符 < ? super E>

語(yǔ)法: <? super E>

舉例 :<? super Dog> 可以傳入的實(shí)參的類型是Dog或者Dog的父類類型

兩大原則

  • add:允許添加E和E的子類元素!
  • get:可以獲取元素,但傳入的類型可能是E到Object之間的任何類型,也就無(wú)法確定接收到數(shù)據(jù)類型,所以返回只能使用Object引用來(lái)接受!如果需要自己的類型則需要強(qiáng)制類型轉(zhuǎn)換。

示例

 public static void method(List<? super Dog> lists){
        //錯(cuò)誤 因?yàn)槟悴恢溃康降咨额愋?
        Animal animal = lists.get(0);
        //正確 只能用Object類接收
        Object object = lists.get(1);
        //錯(cuò)誤 不能用?接收
        ? t = lists.get(2);
        //錯(cuò)誤
        lists.add(object);
        //錯(cuò)誤
        lists.add(new Animal());
        //正確
        lists.add(new Dog());
        //正確 可以存放null元素
        lists.add(null);
    }

六、什么是PECS原則?

PECS原則:生產(chǎn)者(Producer)使用extends,消費(fèi)者(Consumer)使用super。

原則

  • 如果想要獲取,而不需要寫值則使用" ? extends T "作為數(shù)據(jù)結(jié)構(gòu)泛型。
  • 如果想要寫值,而不需要取值則使用" ? super T "作為數(shù)據(jù)結(jié)構(gòu)泛型。

示例-

public class PESC {
    ArrayList<? extends Animal> exdentAnimal;
    ArrayList<? super Animal> superAnimal;
    Dog dog = new Dog("小黑", "黑色");

    private void test() {
        //正確 
        Animal a1 = exdentAnimal.get(0);
        //錯(cuò)誤 
        Animal a2 = superAnimal.get(0);

        //錯(cuò)誤 
        exdentAnimal.add(dog);
        //正確 
        superAnimal.add(dog);
    }
}

示例二

Collections集合工具類有個(gè)copy方法,我們可以看下源碼,就是PECS原則。

 public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        int srcSize = src.size();
        if (srcSize > dest.size())
            throw new IndexOutOfBoundsException("Source does not fit in dest");

        if (srcSize < COPY_THRESHOLD ||
            (src instanceof RandomAccess && dest instanceof RandomAccess)) {
            for (int i=0; i<srcSize; i++)
                dest.set(i, src.get(i));
        } else {
            ListIterator<? super T> di=dest.listIterator();
            ListIterator<? extends T> si=src.listIterator();
            for (int i=0; i<srcSize; i++) {
                di.next();
                di.set(si.next());
            }
        }
    }

我們按照這個(gè)源碼簡(jiǎn)單改造下

public class CollectionsTest {
    
    /**
     * 將源集合數(shù)據(jù)拷貝到目標(biāo)集合
     *
     * @param dest 目標(biāo)集合
     * @param src  源集合
     * @return 目標(biāo)集合
     */
    public static <T> void copy(List<? super T> dest, List<? extends T> src) {
        int srcSize = src.size();
        for (int i = 0; i < srcSize; i++) {
            dest.add(src.get(i));
        }
    }
    
    public static void main(String[] args) {
        ArrayList<Animal> animals = new ArrayList();
        ArrayList<Pig> pigs = new ArrayList();
        pigs.add(new Pig("黑豬", "黑色"));
        pigs.add(new Pig("花豬", "花色"));

        CollectionsTest.copy(animals, pigs);
        System.out.println("dest = " + animals);
    }
    
}

運(yùn)行結(jié)果

dest = [Animal(name=黑豬, color=黑色), Animal(name=花豬, color=花色)]

七、通過一個(gè)案例來(lái)理解 ?和 T 和 Object 的區(qū)別

1、實(shí)體轉(zhuǎn)換

我們?cè)趯?shí)際開發(fā)中,經(jīng)常進(jìn)行實(shí)體轉(zhuǎn)換,比如SO轉(zhuǎn)DTO,DTO轉(zhuǎn)DO等等,所以需要一個(gè)轉(zhuǎn)換工具類。

如下示例

/**
 *  實(shí)體轉(zhuǎn)換工具類
 *  
 *  TODO 說(shuō)明該工具類不能直接用于生產(chǎn),因?yàn)闉榱舜a看去清爽點(diǎn),我少了一些必要檢驗(yàn),所以如果直接拿來(lái)使用可以會(huì)在某些場(chǎng)景下會(huì)報(bào)錯(cuò)。
 */
public class EntityUtil {
  
    /**
     * 集合實(shí)體轉(zhuǎn)換
     *
     * @param target 目標(biāo)實(shí)體類
     * @param list   源集合
     * @return 裝有目標(biāo)實(shí)體的集合
     */
    public static <T> List<T> changeEntityList(Class<T> target, List<?> list) throws Exception {
        if (list == null || list.size() == 0) {
            return null;
        }
        List<T> resultList = new ArrayList<T>();
        //用Object接收
        for (Object obj : list) {
            resultList.add(changeEntityNew(target, obj));
        }
        return resultList;
    }
    /**
     * 實(shí)體轉(zhuǎn)換
     *
     * @param target 目標(biāo)實(shí)體class對(duì)象
     * @param baseTO 源實(shí)體
     * @return 目標(biāo)實(shí)體
     */
    public static <T> T changeEntity(Class<T> target, Object baseTO) throws Exception{
        T obj = target.newInstance();
        if (baseTO == null) {
            return null;
        }
        BeanUtils.copyProperties(baseTO, obj);
        return obj;
    }
}

使用工具類示例

 private void  changeTest() throws Exception {
        ArrayList<Pig> pigs = new ArrayList();
        pigs.add(new Pig("黑豬", "黑色"));
        pigs.add(new Pig("花豬", "花色"));
        //實(shí)體轉(zhuǎn)換
        List<Animal> animals = EntityUtil.changeEntityList(Animal.class, pigs);
    }

這是一個(gè)很好的例子,從這個(gè)例子中我們可以去理解 ?和 T 和 Object的使用場(chǎng)景。

我們先以集合轉(zhuǎn)換來(lái)說(shuō)

public static <T> List<T> changeEntityListNew(Class<T> target, List<?> list);

首先其實(shí)我們并不關(guān)心傳進(jìn)來(lái)的集合內(nèi)是什么對(duì)象,我們只關(guān)系我們需要轉(zhuǎn)換的集合內(nèi)是什么對(duì)象,所以我們傳進(jìn)來(lái)的集合就可以用List<?>表示任何對(duì)象的集合都可以。

返回呢,這里指定的是Class<T>,也就是返回最終是List<T>集合。

再以實(shí)體轉(zhuǎn)換方法為例

public static <T> T changeEntityNew(Class<T> target, Object baseTO)

同樣的,我們并不關(guān)心源對(duì)象是什么,我們只關(guān)心需要轉(zhuǎn)換的對(duì)象,只需關(guān)心需要轉(zhuǎn)換的對(duì)象為T。

那為什么這里用Object上面用?呢,其實(shí)上面也可以改成List<Object> list,效果是一樣的,上面List<?> list在遍歷的時(shí)候最終不就是用Object接收的嗎

?和Object的區(qū)別

?類型不確定和Object作用差不多,好多場(chǎng)景下可以通用,但?可以縮小泛型的范圍,如:List<? extends Animal>,指定了范圍只能是Animal的子類,但是用List<Object>,沒法做到縮小范圍。

總結(jié)

  • 只用于讀功能時(shí),泛型結(jié)構(gòu)使用<? extends T>
  • 只用于寫功能時(shí),泛型結(jié)構(gòu)使用<? super T>
  • 如果既用于寫,又用于讀操作,那么直接使用<T>
  • 如果操作與泛型類型無(wú)關(guān),那么使用<?>

到此這篇關(guān)于Java 泛型中的通配符的文章就介紹到這了,更多相關(guān)Java 泛型通配符內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java微信公眾號(hào)支付示例詳解

    java微信公眾號(hào)支付示例詳解

    這篇文章主要為大家詳細(xì)介紹了java微信公眾號(hào)支付示例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • Java switch關(guān)鍵字原理及用法詳解

    Java switch關(guān)鍵字原理及用法詳解

    這篇文章主要介紹了Java中 switch關(guān)鍵原理及用法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11
  • 詳述IntelliJ IDEA 中自動(dòng)生成 serialVersionUID 的方法(圖文)

    詳述IntelliJ IDEA 中自動(dòng)生成 serialVersionUID 的方法(圖文)

    本篇文章主要介紹了詳述IntelliJ IDEA 中自動(dòng)生成 serialVersionUID 的方法(圖文),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。
    2017-11-11
  • Spring中基于XML的面向切面編程(AOP)詳解

    Spring中基于XML的面向切面編程(AOP)詳解

    這篇文章主要詳細(xì)介紹了Spring中基于XML的面向切面編程(AOP),文中通過代碼示例給大家講解的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2024-04-04
  • Java中的JPA實(shí)體關(guān)系:JPA一對(duì)一,一對(duì)多(多對(duì)一),多對(duì)多

    Java中的JPA實(shí)體關(guān)系:JPA一對(duì)一,一對(duì)多(多對(duì)一),多對(duì)多

    Java Persistence API(JPA)是Java平臺(tái)上的一個(gè)對(duì)象關(guān)系映射(ORM)規(guī)范,用于簡(jiǎn)化數(shù)據(jù)庫(kù)操作,其中實(shí)體關(guān)系的映射是核心內(nèi)容之一,本文將深入淺出地探討JPA中的三種基本實(shí)體關(guān)系類型:一對(duì)一、一對(duì)多、多對(duì)多,揭示常見問題、易錯(cuò)點(diǎn)及其避免策略,希望能幫助大家
    2024-06-06
  • JavaWeb實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)(3)

    JavaWeb實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)(3)

    這篇文章主要為大家詳細(xì)介紹了JavaWeb實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)第三篇,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Spring Data JPA實(shí)現(xiàn)動(dòng)態(tài)條件與范圍查詢實(shí)例代碼

    Spring Data JPA實(shí)現(xiàn)動(dòng)態(tài)條件與范圍查詢實(shí)例代碼

    本篇文章主要介紹了Spring Data JPA實(shí)現(xiàn)動(dòng)態(tài)條件與范圍查詢實(shí)例代碼,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2017-06-06
  • 解決@DateTimeFormat格式化時(shí)間出錯(cuò)問題

    解決@DateTimeFormat格式化時(shí)間出錯(cuò)問題

    這篇文章主要介紹了解決@DateTimeFormat格式化時(shí)間出錯(cuò)問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • JAVA中三種常用的循環(huán)語(yǔ)句詳解

    JAVA中三種常用的循環(huán)語(yǔ)句詳解

    這篇文章主要介紹了JAVA中三種常用的循環(huán)語(yǔ)句詳解,包括格式和執(zhí)行流程,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • Java8 Comparator源碼演示及解析

    Java8 Comparator源碼演示及解析

    這篇文章主要介紹了Java8 Comparator源碼演示及解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11

最新評(píng)論

乳源| 武川县| 平武县| 长兴县| 南漳县| 彩票| 勃利县| 高陵县| 都兰县| 施秉县| 资中县| 兴化市| 牙克石市| 社会| 义马市| 乡宁县| 高平市| 普定县| 铁岭市| 保康县| 博野县| 无锡市| 林甸县| 萨嘎县| 松滋市| 永川市| 峡江县| 旬邑县| 上栗县| 务川| 北海市| 德昌县| 沙田区| 康乐县| 鄄城县| 鹰潭市| 元氏县| 三门县| 英德市| 北海市| 上思县|