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

深入分析JAVA 反射和泛型

 更新時(shí)間:2020年06月20日 14:53:16   作者:認(rèn)真對(duì)待世界的小白  
這篇文章主要介紹了JAVA 反射和泛型的的相關(guān)資料,文中講解非常細(xì)致,代碼幫助大家更好的理解和學(xué)習(xí),感興趣的朋友可以了解下

從 JDK5 以后,Java 的 Class 類增加了泛型功能,從而允許使用泛型來限制 Class 類,例如,String.class 的類型實(shí)際上是 Class<String>。如果 Class 對(duì)應(yīng)的類暫時(shí)未知,則使用 Class<?>。通過在反射中使用泛型,可以避兔使用反射生成的對(duì)象需要強(qiáng)制類型轉(zhuǎn)換。

泛型和 Class 類

使用 Class<T> 泛型可以避免強(qiáng)制類型轉(zhuǎn)換。例如,下面提供一個(gè)簡(jiǎn)單的對(duì)象工廠,該對(duì)象工廠可以根據(jù)指定類來提供該類的實(shí)例。

public class CrazyitObjectFactory {
  public static Object getInstance(String clsName) {
    try {
      // 創(chuàng)建指定類對(duì)應(yīng)的Class對(duì)象
      Class cls = Class.forName(clsName);
      // 返回使用該Class對(duì)象所創(chuàng)建的實(shí)例
      return cls.newInstance();
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }
}

上面程序中兩行粗體字代碼根據(jù)指定的字符串類型創(chuàng)建了一個(gè)新對(duì)象,但這個(gè)對(duì)象的類型是 Object,因此當(dāng)需要使用 CrazyitObjectFactory 的 getInstance() 方法來創(chuàng)建對(duì)象時(shí),將會(huì)看到如下代碼:

// 獲取實(shí)例后需要強(qiáng)制類型轉(zhuǎn)換
Date d = (Date)Crazyit.getInstance("java.util.Date");

甚至出現(xiàn)如下代碼:

JFrame f = (JFrame)Crazyit.getInstance("java.util.Date");

上面代碼在編譯時(shí)不會(huì)有任何問題,但運(yùn)行時(shí)將拋出 ClassCastException 異常,因?yàn)槌绦蛟噲D將一個(gè) Date 對(duì)象轉(zhuǎn)換成 JFrame 對(duì)象。

如果將上面的 CrazyitObjectFactory 工廠類改寫成使用泛型后的 Class,就可以避免這種情況。

public class CrazyitObjectFactory2 {
  public static <T> T getInstance(Class<T> cls) {
    try {
      return cls.newInstance();
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  public static void main(String[] args) {
    // 獲取實(shí)例后無須類型轉(zhuǎn)換
    Date d = CrazyitObjectFactory2.getInstance(Date.class);
    JFrame f = CrazyitObjectFactory2.getInstance(JFrame.class);
  }
}

在上面程序的 getInstance() 方法中傳入一個(gè) Class<T> 參數(shù),這是一個(gè)泛型化的 Class 對(duì)象,調(diào)用該 Class 對(duì)象的 newInstance() 方法將返回一個(gè) T 對(duì)象,如程序中粗體字代碼所示。接下來當(dāng)使用 CrazyitObjectFactory2 工廠類的 getInstance() 方法來產(chǎn)生對(duì)象時(shí),無須使用強(qiáng)制類型轉(zhuǎn)換,系統(tǒng)會(huì)執(zhí)行更嚴(yán)格的檢查,不會(huì)出現(xiàn) ClassCastException 運(yùn)行時(shí)異常。

前面介紹使用 Array 類來創(chuàng)建數(shù)組時(shí),曾經(jīng)看到如下代碼:

// 使用 Array 的 newInstance 方法來創(chuàng)建一個(gè)數(shù)組
Object arr = Array.newInstance(String.class, 10);

對(duì)于上面的代碼其實(shí)使用并不是非常方便,因?yàn)?newInstance() 方法返回的確實(shí)是一個(gè) String[] 數(shù)組,而不是簡(jiǎn)單的 Object 對(duì)象。如果需要將對(duì)象當(dāng)成 String[] 數(shù)組使用,則必須使用強(qiáng)制類型轉(zhuǎn)換——這是不安全的操作。

為了示范泛型的優(yōu)勢(shì),可以對(duì) Array 的 newInstance() 方法進(jìn)行包裝。

public class CrazyitArray {
  // 對(duì)Array的newInstance方法進(jìn)行包裝
  @SuppressWarnings("unchecked")
  public static <T> T[] newInstance(Class<T> componentType, int length) {
    return (T[]) Array.newInstance(componentType, length); // ①
  }

  public static void main(String[] args) {
    // 使用CrazyitArray的newInstance()創(chuàng)建一維數(shù)組
    String[] arr = CrazyitArray.newInstance(String.class, 10);
    // 使用CrazyitArray的newInstance()創(chuàng)建二維數(shù)組
    // 在這種情況下,只要設(shè)置數(shù)組元素的類型是int[]即可。
    int[][] intArr = CrazyitArray.newInstance(int[].class, 5);
    arr[5] = "瘋狂Java講義";
    // intArr是二維數(shù)組,初始化該數(shù)組的第二個(gè)數(shù)組元素
    // 二維數(shù)組的元素必須是一維數(shù)組
    intArr[1] = new int[] { 23, 12 };
    System.out.println(arr[5]);
    System.out.println(intArr[1][1]);
  }
}

上面程序中粗體字代碼定義的 newInstance() 方法對(duì) Array 類提供的 newInstance() 方法進(jìn)行了包裝,將方法簽名改成了 public static <T> T[] newInstance(Class<T> componentType, int length),這就保證程序通過該 newInstance() 方法創(chuàng)建數(shù)組時(shí)的返回值就是數(shù)組對(duì)象,而不是 Object 對(duì)象,從而避免了強(qiáng)制類型轉(zhuǎn)換。

提示:程序在①行代碼處將會(huì)有一個(gè) unchecked 編譯警告,所以程序使用了 @SuppressWarnings 來抑制這個(gè)警告信息。

使用反射來獲取泛型信息

通過指定類對(duì)應(yīng)的 Class 對(duì)象,可以獲得該類里包含的所有成員變量,不管該成員變量是使用 private 修飾,還是使用 public 修飾。獲得了成員變量對(duì)應(yīng)的 Field 對(duì)象后,就可以很容易地獲得該成員變量的數(shù)據(jù)類型,即使用如下代碼即可獲得指定成員變量的類型。

// 獲取成員變量 f 的類型
Class<?> a = f.getType();

但這種方式只對(duì)普通類型的成員變量有效。如果該成員變量的類型是有泛型類型的類型,如 Map<String, Integer> 類型,則不能誰確地得到該成員變量的泛型參數(shù)。

為了獲得指定成員變量的泛型類型,應(yīng)先使用如下方法來獲取該成員變量的泛型類型。

// 獲得成員變量 f 的泛型類型
Type gType = f.getGenericType();

然后將 Type 對(duì)象強(qiáng)制類型轉(zhuǎn)換為 ParameterizedType 對(duì)象,ParameterizedType 代表被參數(shù)化的類型,也就是增加了泛型限制的類型。ParameterizedType 類提供了如下兩個(gè)方法。

  • getRawType():返回沒有泛型信息的原始類型。
  • getActualTypeArguments():返回泛型參數(shù)的類型。

下面是一個(gè)獲取泛型類型的完整程序。

public class GenericTest {
  private Map<String, Integer> score;

  public static void main(String[] args) throws Exception {
    Class<GenericTest> clazz = GenericTest.class;
    Field f = clazz.getDeclaredField("score");
    // 直接使用getType()取出的類型只對(duì)普通類型的成員變量有效
    Class<?> a = f.getType();
    // 下面將看到僅輸出java.util.Map
    System.out.println("score的類型是:" + a);
    // 獲得成員變量f的泛型類型
    Type gType = f.getGenericType();
    // 如果gType類型是ParameterizedType對(duì)象
    if (gType instanceof ParameterizedType) {
      // 強(qiáng)制類型轉(zhuǎn)換
      ParameterizedType pType = (ParameterizedType) gType;
      // 獲取原始類型
      Type rType = pType.getRawType();
      System.out.println("原始類型是:" + rType);
      // 取得泛型類型的泛型參數(shù)
      Type[] tArgs = pType.getActualTypeArguments();
      System.out.println("泛型信息是:");
      for (int i = 0; i < tArgs.length; i++) {
        System.out.println("第" + i + "個(gè)泛型類型是:" + tArgs[i]);
      }
    } else {
      System.out.println("獲取泛型類型出錯(cuò)!");
    }
  }
}

上面程序中的粗體字代碼就是取得泛型類型的關(guān)鍵代碼。運(yùn)行上面程序,將看到如下運(yùn)行結(jié)果:

score的類型是:interface java.util.Map
原始類型是:interface java.util.Map
泛型信息是:
第0個(gè)泛型類型是:class java.lang.String
第1個(gè)泛型類型是:class java.lang.Integer

從上面的運(yùn)行結(jié)果可以看出,使用 getType() 方法只能獲取普通類型的成員變量的數(shù)據(jù)類型:對(duì)于增加了泛型的成員變量,應(yīng)該使用 getGenericType() 方法來取得其類型。

提示:Type 也是 java.lang.reflect 包下的一個(gè)接口,該接口代表所有類型的公共高級(jí)接口,Class 是 Type 接口的實(shí)現(xiàn)類。Type 包括原始類型、參數(shù)化類型、數(shù)組類型、類型變量和基本類型等。

以上就是深入分析JAVA 反射和泛型的詳細(xì)內(nèi)容,更多關(guān)于JAVA 反射和泛型的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 妙用Java8中的Function接口消滅if...else

    妙用Java8中的Function接口消滅if...else

    在開發(fā)過程中經(jīng)常會(huì)使用if...else...進(jìn)行判斷拋出異常、分支處理等操作。這些if...else...充斥在代碼中嚴(yán)重影響了代碼代碼的美觀,本文就妙用Java8中的Function接口消滅if...else,感興趣的可以了解一下
    2022-01-01
  • SpringBoot項(xiàng)目使用?axis?調(diào)用webservice接口的實(shí)踐記錄

    SpringBoot項(xiàng)目使用?axis?調(diào)用webservice接口的實(shí)踐記錄

    這篇文章主要介紹了SpringBoot項(xiàng)目使用?axis?調(diào)用webservice接口,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • SpringCloud的@RefreshScope 注解你了解嗎

    SpringCloud的@RefreshScope 注解你了解嗎

    這篇文章主要介紹了Spring Cloud @RefreshScope 原理及使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-09-09
  • java線程之Happens before規(guī)則案例詳解

    java線程之Happens before規(guī)則案例詳解

    這篇文章主要為大家介紹了java線程之Happens-before規(guī)則,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>
    2022-08-08
  • Spring項(xiàng)目中swagger用法與swagger-ui使用

    Spring項(xiàng)目中swagger用法與swagger-ui使用

    這篇文章主要介紹了Spring項(xiàng)目中swagger用法與swagger-ui使用,通過圖文并茂的形式給大家介紹了編寫springboot項(xiàng)目的方法及導(dǎo)入spring-fox依賴的代碼詳解,需要的朋友可以參考下
    2021-05-05
  • Java使用Statement接口執(zhí)行SQL語句操作實(shí)例分析

    Java使用Statement接口執(zhí)行SQL語句操作實(shí)例分析

    這篇文章主要介紹了Java使用Statement接口執(zhí)行SQL語句操作,結(jié)合實(shí)例形式詳細(xì)分析了Java使用Statement接口針對(duì)mysql數(shù)據(jù)庫進(jìn)行連接與執(zhí)行SQL語句增刪改查等相關(guān)操作技巧與注意事項(xiàng),需要的朋友可以參考下
    2018-07-07
  • 關(guān)于MyBatis Plus中使用or和and問題

    關(guān)于MyBatis Plus中使用or和and問題

    這篇文章主要介紹了關(guān)于MyBatis Plus中使用or和and問題,需要的朋友可以參考下
    2020-12-12
  • Spring深入探索AOP切面編程

    Spring深入探索AOP切面編程

    Spring是一個(gè)廣泛應(yīng)用的框架,SpringAOP則是Spring提供的一個(gè)標(biāo)準(zhǔn)易用的aop框架,依托Spring的IOC容器,提供了極強(qiáng)的AOP擴(kuò)展增強(qiáng)能力,對(duì)項(xiàng)目開發(fā)提供了極大地便利
    2022-07-07
  • SpringBoot3.x循環(huán)依賴問題解決方案

    SpringBoot3.x循環(huán)依賴問題解決方案

    這篇文章主要介紹了SpringBoot3.x循環(huán)依賴的相關(guān)知識(shí),本文通過示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-06-06
  • Java的常用包

    Java的常用包

    本文主要對(duì)Java的常用包進(jìn)行一一介紹。具有一定的參考價(jià)值,下面跟著小編一起來看下吧
    2017-01-01

最新評(píng)論