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

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

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

什么是泛型

Java泛型(generics)是JDK5中引入的一個新特性,泛型提供了 編譯時類型安全監(jiān)測機制,該機制允許我們在編譯時檢測到非法的類型數(shù)據(jù)結構。泛型的本質(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轉為String,因此程序報錯;

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

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

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

泛型的使用

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

泛型類

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

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

    public Generic(T key) { //泛型構造方法形參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();
}

當實現(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;
    }
}

當實現(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可以為任意類型??梢灶愋团cT相同,也可以不同。
        //由于泛型方法在聲明的時候會聲明泛型<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是否可以看成具有父子關系的泛型類型呢?

為了弄清楚這個問題,我們使用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'

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

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

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

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

通配符上限

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

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

通配符下限

//結構
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) { //泛型構造方法形參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());
        }
    }

結果

key:Object

在這里插入圖片描述

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

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

那么,打印結果就是

key:Number

在這里插入圖片描述

且對應的方法

在這里插入圖片描述

如果是接口類型的泛型

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());
        }
    }
}

打印結果

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僅僅是當做一種符號,即占位符去使用,沒有實際意義,所以如果你試圖同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ān)測機制,最典型的就是各種容器類如:List、Set、Map,通過泛型在編譯期間限制添加元素的類型
  • 增強代碼規(guī)范性&復用性,例如設計模式模板方法模式結合泛型來使用,在模板方法中使用泛型,可以增強模板方法的復用性

泛型的類型問題

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

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

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

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

相關文章

  • Java延時的3種實現(xiàn)方法舉例

    Java延時的3種實現(xiàn)方法舉例

    這篇文章主要給大家介紹了關于Java延時的3種實現(xiàn)方法舉例,java開發(fā)中常會用到延時任務,文中通過實例代碼介紹的非常詳細,對大家的學習具有一定參考借鑒價值,需要的朋友可以參考下
    2023-07-07
  • 關于@Query注解的用法(Spring Data JPA)

    關于@Query注解的用法(Spring Data JPA)

    這篇文章主要介紹了關于@Query注解的用法(Spring Data JPA),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • SpringBoot使用validation做參數(shù)校驗說明

    SpringBoot使用validation做參數(shù)校驗說明

    這篇文章主要介紹了SpringBoot使用validation做參數(shù)校驗說明,首先通過添加hibernate-validator展開全文內(nèi)容,具有一定的參考價值,需要的小伙伴可以參考與喜愛
    2022-04-04
  • Java中的Lombok使用及工作原理詳解

    Java中的Lombok使用及工作原理詳解

    這篇文章主要介紹了Java中的Lombok使用及工作原理詳解,Lombok是一個Java庫,能自動插入編輯器并構建工具,簡化Java開發(fā),通過添加注解的方式,不需要為類編寫getter或eques方法,同時可以自動化日志變量,需要的朋友可以參考下
    2023-10-10
  • 詳解Kotlin中的面向對象(一)

    詳解Kotlin中的面向對象(一)

    這篇文章主要介紹了詳解Kotlin中的面向對象(一)的相關資料,需要的朋友可以參考下
    2017-06-06
  • Spring切面優(yōu)先級與基于xml的AOP實現(xiàn)方法詳解

    Spring切面優(yōu)先級與基于xml的AOP實現(xiàn)方法詳解

    這篇文章主要介紹了Spring切面的優(yōu)先級與基于xml的AOP的詳細步驟,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-11-11
  • java實現(xiàn)消息隊列的兩種方式(小結)

    java實現(xiàn)消息隊列的兩種方式(小結)

    本文主要介紹了兩種java實現(xiàn)消息隊列的方式,利用Spring消息模板發(fā)送消息和Apache ActiveMQ官方實例發(fā)送消息,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-12-12
  • Springmvc如何返回xml及json格式數(shù)據(jù)

    Springmvc如何返回xml及json格式數(shù)據(jù)

    這篇文章主要介紹了Springmvc如何返回xml及json格式數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-09-09
  • Java泛型的簡單實例

    Java泛型的簡單實例

    這篇文章介紹了Java泛型的簡單實例,有需要的朋友可以參考一下
    2013-10-10
  • Spring Boot集成Thymeleaf模板引擎的完整步驟

    Spring Boot集成Thymeleaf模板引擎的完整步驟

    這篇文章主要給大家介紹了關于Spring Boot集成Thymeleaf模板引擎的完整步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-02-02

最新評論

金堂县| 年辖:市辖区| 普陀区| 温州市| 前郭尔| 渝北区| 惠州市| 榕江县| 巩留县| 孝义市| 信丰县| 浦城县| 石景山区| 平阳县| 通城县| 南昌市| 喀喇沁旗| 长泰县| 滦南县| 康定县| 泰安市| 靖远县| 繁昌县| 米泉市| 浦江县| 沾益县| 临汾市| 英德市| 罗平县| 龙门县| 德州市| 南丹县| 惠水县| 宿迁市| 邵武市| 宁海县| 藁城市| 大港区| 平度市| 黄平县| 富阳市|