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

2022最新Java泛型詳解(360度無死角介紹)

 更新時間:2022年10月09日 15:47:36   作者:垃圾王子晗  
Java泛型(generics)是JDK5中引入的一個新特性,泛型提供了 編譯時類型安全監(jiān)測機制,該機制允許我們在編譯時檢測到非法的類型數(shù)據(jù)結(jié)構(gòu),這篇文章主要介紹了java泛型的基本概念及使用詳解,感興趣的朋友跟隨小編一起看看吧

什么是泛型

Java泛型(generics)是JDK5中引入的一個新特性,泛型提供了 編譯時類型安全監(jiān)測機制,該機制允許我們在編譯時檢測到非法的類型數(shù)據(jù)結(jié)構(gòu)。泛型的本質(zhì)就是 參數(shù)化類型,也就是所操作的數(shù)據(jù)類型被指定為一個參數(shù)。

重點概念1:泛型的作用域是在編譯期間

List<String> stringArrayList = new ArrayList<String>();
List<Integer> integerArrayList = new ArrayList<Integer>();

Class classStringArrayList = stringArrayList.getClass();
Class classIntegerArrayList = integerArrayList.getClass();

if(classStringArrayList.equals(classIntegerArrayList)){
    System.out.println("泛型測試類型相同");
}

重點概念2:泛型主要作用是在編譯期間提供類型安全監(jiān)測機制

List arrayList = new ArrayList();
arrayList.add("aaaa");
arrayList.add(100);

for(int i = 0; i< arrayList.size();i++){
    String item = (String)arrayList.get(i);
    System.out.println("泛型測試item = " +item);
}
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

ArrayList可以存放任意類型,例子中首次添加了一個Sring類型,那么arrayList 再使用時都以String的方式使用,此時再次添加一個Integer類型的變量100,arrayList 只能嘗試將Integer類型的變量100轉(zhuǎn)為String,因此程序報錯;

為了解決類似這樣的問題,泛型應(yīng)運而生,我們將第一行聲明初始化list的代碼更改一下,編譯器會在編譯階段就能夠幫我們發(fā)現(xiàn)類似這樣的問題。

List<String> arrayList = new ArrayList<String>();
...
//arrayList.add(100); 在編譯階段,編譯器就會報錯

綜上可知:泛型類型在邏輯上看以看成是多個不同的類型,實際上都是相同的基本類型。

泛型的使用

泛型有三種使用方式,分別為:泛型類、泛型接口、泛型方法

泛型類

泛型類型用于類的定義中,被稱為泛型類。通過泛型可以完成對一組類的操作對外開放相同的接口。最典型的就是各種容器類,如:List、Set、Map。

//此處T可以隨便寫為任意標(biāo)識,常見的如T、E、K、V等形式的參數(shù)常用于表示泛型
//在實例化泛型類時,必須指定T的具體類型
public class Generic<T>{ 
    //key這個成員變量的類型為T,T的類型由外部指定  
    private T key;

    public Generic(T key) { //泛型構(gòu)造方法形參key的類型也為T,T的類型由外部指定
        this.key = key;
    }

     public <T,K> T showKeyName(Generic<T> container){
        System.out.println("container key :" + container.getKey());
        T test = container.getKey();
        return test;
    }


    public T getKey(){ //泛型方法getKey的返回值類型為T,T的類型由外部指定
        return key;
    }
}
//泛型的類型參數(shù)只能是類類型(包括自定義類),不能是簡單類型
//傳入的實參類型需與泛型的類型參數(shù)類型相同,即為Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);

//傳入的實參類型需與泛型的類型參數(shù)類型相同,即為String.
Generic<String> genericString = new Generic<String>("key_vlaue");
Log.d("泛型測試","key is " + genericInteger.getKey());
Log.d("泛型測試","key is " + genericString.getKey());

泛型接口

//定義一個泛型接口
public interface Generator<T> {
    public T next();
}

當(dāng)實現(xiàn)泛型接口的類,未傳入泛型實參時

/**
 * 未傳入泛型實參時,與泛型類的定義相同,在聲明類的時候,需將泛型的聲明也一起加到類中
 * 即:class FruitGenerator<T> implements Generator<T>{
 * 如果不聲明泛型,如:class FruitGenerator implements Generator<T>,編譯器會報錯:"Unknown class"
 */
class FruitGenerator<T> implements Generator<T>{
    @Override
    public T next() {
        return null;
    }
}

當(dāng)實現(xiàn)泛型接口的類,傳入泛型實參時:

/**
 * 傳入泛型實參時:
 * 定義一個生產(chǎn)器實現(xiàn)這個接口,雖然我們只創(chuàng)建了一個泛型接口Generator<T>
 * 但是我們可以為T傳入無數(shù)個實參,形成無數(shù)種類型的Generator接口。
 * 在實現(xiàn)類實現(xiàn)泛型接口時,如已將泛型類型傳入實參類型,則所有使用泛型的地方都要替換成傳入的實參類型
 * 即:Generator<T>,public T next();中的的T都要替換成傳入的String類型。
 */
public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

泛型方法

泛型類,是在實例化類的時候指明泛型的具體類型;泛型方法,是在調(diào)用方法的時候指明泛型的具體類型 。

例如上述Generic類中兩個方法

 public T getKey(){
     return key;
 }

雖然在方法中使用了泛型,但是這并不是一個泛型方法。這只是類中一個普通的成員方法,只不過他的返回值是在聲明泛型類已經(jīng)聲明過的泛型。所以在這個方法中才可以繼續(xù)使用 T 這個泛型。

public <T,K> T showKeyName(Generic<T> container){
    System.out.println("container key :" + container.getKey());
    T test = container.getKey();
    return test;
}

這才是一個真正的泛型方法。 首先在public與返回值之間的必不可少,這表明這是一個泛型方法,并且聲明了一個泛型T這個T可以,出現(xiàn)在這個泛型方法的任意位置,泛型的數(shù)量也可以為任意多個

泛型類中的泛型方法

public class GenericFruit {
    class Fruit{
        @Override
        public String toString() {
            return "fruit";
        }
    }

    class Apple extends Fruit{
        @Override
        public String toString() {
            return "apple";
        }
    }

    class Person{
        @Override
        public String toString() {
            return "Person";
        }
    }

    class GenerateTest<T>{
        public void show_1(T t){
            System.out.println(t.toString());
        }

        //在泛型類中聲明了一個泛型方法,使用泛型E,這種泛型E可以為任意類型。可以類型與T相同,也可以不同。
        //由于泛型方法在聲明的時候會聲明泛型<E>,因此即使在泛型類中并未聲明泛型,編譯器也能夠正確識別泛型方法中識別的泛型。
        public <E> void show_3(E t){
            System.out.println(t.toString());
        }

        //在泛型類中聲明了一個泛型方法,使用泛型T,注意這個T是一種全新的類型,可以與泛型類中聲明的T不是同一種類型。
        public <T> void show_2(T t){
            System.out.println(t.toString());
        }
    }

    public static void main(String[] args) {
        Apple apple = new Apple();
        Person person = new Person();

        GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
        //apple是Fruit的子類,所以這里可以
        generateTest.show_1(apple);
        //編譯器會報錯,因為泛型類型實參指定的是Fruit,而傳入的實參類是Person
        //generateTest.show_1(person);

        //使用這兩個方法都可以成功
        generateTest.show_2(apple);
        generateTest.show_2(person);

        //使用這兩個方法也都可以成功
        generateTest.show_3(apple);
        generateTest.show_3(person);
    }
}

泛型通配符

Ingeter是Number的一個子類,Generic與Generic實際上是相同的一種基本類型。那么問題來了,在使用Generic作為形參的方法中,能否使用Generic的實例傳入呢?在邏輯上類似于Generic和Generic是否可以看成具有父子關(guān)系的泛型類型呢?

為了弄清楚這個問題,我們使用Generic這個泛型類繼續(xù)看下面的例子:

public void showKeyValue1(Generic<Number> obj){
    Log.d("泛型測試","key value is " + obj.getKey());
}
Generic<Integer> gInteger = new Generic<Integer>(123);
Generic<Number> gNumber = new Generic<Number>(456);

showKeyValue(gNumber);
showKeyValue(gInteger);

showKeyValue(gInteger);這個方法編譯器會為我們報錯:

Generic<java.lang.Integer> cannot be applied to Generic<java.lang.Number>

而且如果我們用重載的思維再定義一個Generic<java.lang.Integer>類型的方法

    public static void showKeyValue1(Generic<Integer> obj){
        System.out.println("泛型測試,key value is " + obj.getKey());

        System.out.println("泛型測試類型相同");
    }

    public static void showKeyValue1(Generic<Integer> obj){
        System.out.println("泛型測試,key value is " + obj.getKey());

        System.out.println("泛型測試類型相同");
    }

會報錯重載異常

'showKeyValue1(Generic<Integer>)' is already defined in 'com.wzh.demo.test.FruitGenerator'

這其實是印證了上述的結(jié)論:泛型類型在邏輯上看以看成是多個不同的類型,實際上都是相同的基本類型,也就是Generic<java.lang.Integer>和Generic<java.lang.Number>本質(zhì)都是Generic,但編譯期間方法入?yún)⒈仨氈付ǖ姆盒皖?,因?strong>同一種泛型可以對應(yīng)多個版本(因為參數(shù)類型是不確定的),不同版本的泛型類實例是不兼容的。

解決方案,使用泛型通配符?

public void showKeyValue1(Generic<?> obj){
    Log.d("泛型測試","key value is " + obj.getKey());
}

類型通配符一般是使用?代替具體的類型實參,注意此處**’?’是類型實參,而不是類型形參** ,再直白點的意思就是,此處的?和Number、String、Integer一樣都是一種實際的類型,可以把?看成所有類型的父類。是一種真實的類型。當(dāng)具體類型不確定的時候。那么可以用 ? 通配符來表未知類型。

通配符上限

//結(jié)構(gòu)
public class XxxClass<T extend XxxClass>
//案例
public void showKeyValue1(Generic<? extend Number> obj){
    Log.d("泛型測試","key value is " + obj.getKey());
}

此時限定傳參的泛型類只能是Number或者Number的子類

通配符下限

//結(jié)構(gòu)
public class XxxClass<T super XxxClass>
//案例
public void showKeyValue1(Generic<? super Number> obj){
    Log.d("泛型測試","key value is " + obj.getKey());
}

此時限定傳參的泛型類只能是Number或者Number的父類

類型擦除

public class Erasure<T>{
    //key這個成員變量的類型為T,T的類型由外部指定  
    private T key;

    public Erasure(T key) { //泛型構(gòu)造方法形參key的類型也為T,T的類型由外部指定
        this.key = key;
    }

    public T getKey(){ //泛型方法getKey的返回值類型為T,T的類型由外部指定
        return key;
    }


    public <T extends List> T show(T t){ //泛型方法getKey的返回值類型為T,T的類型由外部指定
        return t;
    }

}

我們從程序運行期間來看

public static void main(String[] args) {
        Erasure<Number> gNumber = new Erasure<Number>(456);

        Class<? extends Erasure> aClass = gNumber.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField.getName() + ":" +declaredField.getType().getSimpleName());
        }
    }

結(jié)果

key:Object

在這里插入圖片描述

如果我們給泛型加一個類型通配符上限

public class Erasure<T extends Number>{....}

那么,打印結(jié)果就是

key:Number

在這里插入圖片描述

且對應(yīng)的方法

在這里插入圖片描述

如果是接口類型的泛型

interface Info<T> {

    public T info(T t);
}

public class InfoImpl implements Info<Integer>{

    @Override
    public Integer info(Integer var) {
        return var;
    }

    public static void main(String[] args) {
        Class<InfoImpl> infoClass = InfoImpl.class;
        Method[] declaredMethods = infoClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod.getName() + ":" + declaredMethod.getReturnType().getSimpleName());
        }
    }
}

打印結(jié)果

main:void
info:Integer
info:Object

在這里插入圖片描述

個人備注一個疑問點,如上圖所示類型擦除后會生成一個Integer和Object類型的info方法,但為何我通過反射調(diào)用Object類型的info方法時候會有報錯呢?

public class InfoImpl implements Info<Integer>{

    @Override
    public Integer info(Integer var) {
        return var;
    }

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<InfoImpl> infoClass = InfoImpl.class;
        Method[] declaredMethods = infoClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("methodNameAndType:" +declaredMethod.getName() + ":" + declaredMethod.getReturnType().getSimpleName());
            if(declaredMethod.getReturnType().getSimpleName().equals("Object")){
                InfoImpl info = infoClass.newInstance();
//                System.out.println("methodName:"  +declaredMethod.getName());
                Arrays.stream(declaredMethod.getParameterTypes()).forEach(param -> {
                    System.out.println("param:" +  param);
                });
                System.out.println("method return :" +declaredMethod.getReturnType());
                String str= "abc";
                Object invoke = declaredMethod.invoke(info, str);
                System.out.println(invoke);
            }
        }
    }
}

易混點強調(diào):由于泛型擦除機制,T僅僅是當(dāng)做一種符號,即占位符去使用,沒有實際意義,所以如果你試圖同T t = new T();實例化T類型的對象是通不過編譯的

class GenericsA<T>
{
    T t = new T(); // Error
}

但是我們可以通過反射來完成這一需求

 public T createT(Class<T> tClass) throws IllegalAccessException, InstantiationException {
     return tClass.newInstance();
 }

泛型與數(shù)組

可以聲明帶泛型的數(shù)據(jù)引用,但不能直接創(chuàng)建帶泛型的數(shù)組對象

小總結(jié)

泛型的作用域:編譯期間;

泛型的作用

  • 編譯時提供類型安全監(jiān)測機制,最典型的就是各種容器類如:List、Set、Map,通過泛型在編譯期間限制添加元素的類型
  • 增強代碼規(guī)范性&復(fù)用性,例如設(shè)計模式模板方法模式結(jié)合泛型來使用,在模板方法中使用泛型,可以增強模板方法的復(fù)用性

泛型的類型問題

  • 泛型類型在邏輯上看以看成是多個不同的類型,實際上都是相同的基本類型,可以理解為泛型就是一種作用在編譯期的占位符,過了編譯期,運行期的類型擦除機制會完全擦除泛型類的影響
  • 同一種泛型可以對應(yīng)多個版本(因為參數(shù)類型是不確定的),不同版本的泛型類實例是不兼容的,參考以下報錯來理解
Generic<java.lang.Integer>  cannot be applied to Generic<java.lang.Number>

但我們可以通過泛型通配符Generic<?>

泛型類對比泛型方法
泛型類是在實例化類的時候指明泛型的具體類型;泛型方法是在調(diào)用方法的時候指明泛型的具體類型

到此這篇關(guān)于java泛型360度無死角詳細(xì)講解的文章就介紹到這了,更多相關(guān)java泛型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MyBatis特殊SQL的執(zhí)行實例代碼

    MyBatis特殊SQL的執(zhí)行實例代碼

    這篇文章主要給大家介紹了關(guān)于MyBatis特殊SQL執(zhí)行的相關(guān)資料,文中通過實例代碼和圖文介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用MyBatis具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2023-01-01
  • 使用try-with-resource的輸入輸出流自動關(guān)閉

    使用try-with-resource的輸入輸出流自動關(guān)閉

    這篇文章主要介紹了使用try-with-resource的輸入輸出流自動關(guān)閉方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-07-07
  • JavaBean valication驗證實現(xiàn)方法示例

    JavaBean valication驗證實現(xiàn)方法示例

    這篇文章主要介紹了JavaBean valication驗證實現(xiàn)方法,結(jié)合實例形式分析了JavaBean valication驗證相關(guān)概念、原理、用法及操作注意事項,需要的朋友可以參考下
    2020-03-03
  • SpringBoot詳解shiro過濾器與權(quán)限控制

    SpringBoot詳解shiro過濾器與權(quán)限控制

    當(dāng)shiro被運用到web項目時,shiro會自動創(chuàng)建一些默認(rèn)的過濾器對客戶端請求進行過濾。比如身份驗證、授權(quán)的相關(guān)的,這篇文章主要介紹了shiro過濾器與權(quán)限控制
    2022-07-07
  • 詳解Spring Boot 異步執(zhí)行方法

    詳解Spring Boot 異步執(zhí)行方法

    這篇文章主要介紹了Spring Boot 異步執(zhí)行方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-03-03
  • 關(guān)于idea2020.3升級lombok不能使用的問題

    關(guān)于idea2020.3升級lombok不能使用的問題

    這篇文章主要介紹了關(guān)于idea2020.3升級lombok不能使用的問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • idea啟動springboot報錯: 找不到或無法加載主類問題

    idea啟動springboot報錯: 找不到或無法加載主類問題

    這篇文章主要介紹了idea啟動springboot報錯: 找不到或無法加載主類問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • springboot redis使用lettuce配置多數(shù)據(jù)源的實現(xiàn)

    springboot redis使用lettuce配置多數(shù)據(jù)源的實現(xiàn)

    這篇文章主要介紹了springboot redis使用lettuce配置多數(shù)據(jù)源的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • 混亂的Java日志體系及集成jar包梳理分析

    混亂的Java日志體系及集成jar包梳理分析

    這篇文章主要詳細(xì)的為大家梳理分析了剪不斷理還亂的Java日志體系,以及日志系統(tǒng)涉及到的繁雜的各種集成?jar?包,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-03-03
  • Java實戰(zhàn)網(wǎng)上電子書城的實現(xiàn)流程

    Java實戰(zhàn)網(wǎng)上電子書城的實現(xiàn)流程

    讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+JSP+maven+Mysql實現(xiàn)一個網(wǎng)上電子書城,大家可以在過程中查缺補漏,提升水平
    2022-01-01

最新評論