亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

JAVA運(yùn)行時(shí)的泛型擦除與反序列化的應(yīng)用

weizx / 880人閱讀

摘要:回到的第二方法的用法,通過上面的分析,我們可以知道,方法其實(shí)也是用來獲取泛型的實(shí)際類型的,這樣就可以將響應(yīng)反序列化為帶泛型的類型了。在很多反序列化的開源組件中,都用了這個(gè)原理例如的方法,所以我們會(huì)經(jīng)常見到實(shí)例化的時(shí)候會(huì)多個(gè)花括號(hào)。

前段日子在使用google-http-client.jar 這個(gè)組件做http請(qǐng)求時(shí),發(fā)現(xiàn)一件有趣的事情,具體代碼如下:

       try {
            HttpTransport transport = new NetHttpTransport.Builder().doNotValidateCertificate().build();
            requestFactory = transport.createRequestFactory(new HttpRequestInitializer() {
                @Override
                public void initialize(HttpRequest request) {
                    int timeout = 5 * 1000;
                    request.setReadTimeout(timeout);
                    request.setParser(new JsonObjectParser(new JacksonFactory()));
                    request.setThrowExceptionOnExecuteError(false);
                    logger.debug("set timeout = {} milliseconds", timeout);
                }
            });
        } catch (GeneralSecurityException e) {
            logger.error("init static members failed:", e);
        }
         HttpRequest request = requestFactory.buildPostRequest(new GenericUrl(url), content);
         HttpResponse response  =request.execute();
         Bean ret = (Map)response.parseAs(Bean.class);
         ......

這是一段很簡單的http請(qǐng)求的代碼,引起我注意的是最后一段代碼,并且有個(gè)疑問:
為什么HttpResponse.parseAs方法可以通過入?yún)ean.class就能夠?qū)⒔Y(jié)果裝配到Bean類,并返回Bean類型?
事實(shí)上,HttpResponse.parseAs有兩個(gè)同名的重載方法:

public  T parseAs(Class dataClass) throws IOException {
    if (!hasMessageBody()) {
      return null;
    }
    return request.getParser().parseAndClose(getContent(), getContentCharset(), dataClass);
  }

public Object parseAs(Type dataType) throws IOException {
    if (!hasMessageBody()) {
      return null;
    }
    return request.getParser().parseAndClose(getContent(), getContentCharset(), dataType);
  }

兩個(gè)入?yún)⒉煌?,返回的類型也不同,第一個(gè)方法可以在編譯期返回確切的類型,第二個(gè)只能返回Object類型,需要使用者自行強(qiáng)轉(zhuǎn)。那么這兩個(gè)方法到底有什么區(qū)別呢,既然存在肯定是為了解決什么問題吧。我們來看看這兩個(gè)方法用在哪兒:

1、Bean ret = response.parseAs(Bean.class);
2、Map ret = (Map)response.parseAs(new TypeToken>() {}.getType());

相信已經(jīng)有的朋友已經(jīng)看出來了, 像Map,List這些帶有泛型的類型是無法直接通過.class的靜態(tài)變量獲取的,就算我們可以通過Map.class獲取到,但得到的卻是Map,和Map還是不一樣的。泛型存在于編譯期,在運(yùn)行時(shí)Map和Map的類實(shí)例(Class對(duì)象)是同一個(gè),這是為了防止在運(yùn)行期過多創(chuàng)建類實(shí)例,防止類型膨脹,減少運(yùn)行時(shí)開銷,這樣的實(shí)現(xiàn)不可避免的就需要在運(yùn)行時(shí)將泛型擦除,所以第二個(gè)parseAs方法就是為了動(dòng)態(tài)的在運(yùn)行時(shí)獲取帶泛型的實(shí)際類型,從而反序列化到該類型。泛型在運(yùn)行時(shí)被擦除和在運(yùn)行時(shí)獲取泛型的實(shí)際類型看似矛盾的兩個(gè)問題,前者表述沒有問題,后者在一定條件下也是對(duì)的,為什么這么說,我們來看怎么獲取運(yùn)行時(shí)對(duì)象a的泛型指代的實(shí)際類型,請(qǐng)看如下代碼:

package org.hxb.spring.generic;

import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.Map;

import org.junit.Test;

public class GenericTest {

    @Test
    public void test1() {
        Bean> a = new Bean>();
        System.out.println(a.getClass().getGenericSuperclass().getTypeName());
        ParameterizedType type = (ParameterizedType) a.getClass().getGenericSuperclass();
        if (type.getActualTypeArguments() != null) {
            System.out.println(Arrays.asList(type.getActualTypeArguments()));
        }

    }

    @Test
    public void test2() {
        Bean> a = new Bean>() {
        };
        ParameterizedType type = (ParameterizedType) a.getClass().getGenericSuperclass();
        if (type.getActualTypeArguments() != null) {
            System.out.println(Arrays.asList(type.getActualTypeArguments()));
        }
    }

}

class Father {

}

class Bean extends Father {

}
輸出:
[T]
[java.util.Map]

有人會(huì)問我,為什么Bean要繼承一個(gè)Father? 因?yàn)椴贿@么做會(huì)導(dǎo)致(ParameterizedType)a.getClass().getGenericSuperclass()語句報(bào)cast exception,getGenericSuperclass方法jdk 1.5 之后加入的,返回直接父類,繼承的父類。(泛型也是同期引入的,同期引入的還有接口java.lang.reflect.Type,以及一些和java.lang.Class 同級(jí)別的實(shí)現(xiàn)類如ParameterizedType等),那第二Test為什么可以得到運(yùn)行時(shí)真實(shí)類型?不知道大家也沒有注意到這個(gè)細(xì)微的差別:

 Bean> a = new Bean>();
 Bean> a = new Bean>(){};

下面那句話多了一對(duì)花括號(hào),相信大家都知道這是什么意思,這樣就創(chuàng)建了一個(gè)匿名類,

第一種方法顯示a的類型是Bean

第一種方法顯示a的類型是GenericTest$1

匿名類繼承類型Bean>,而這個(gè)匿名類是在運(yùn)行時(shí)定義的,所以保留了泛型的實(shí)際類型(實(shí)際就是相當(dāng)于Bean extends Father,此時(shí)繼承的是確定類型)
所以getGenericSuperclass方法返回一個(gè)ParameterizedType的結(jié)果,然后通過ParameterizedType的getActualTypeArguments方法便可以獲取實(shí)際的類型,實(shí)際上用這種方法的話Bean就無需在編譯器繼承某個(gè)父類了,直接在運(yùn)行時(shí)聲明一個(gè)匿名類即可:

package org.hxb.spring.generic;

import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.Map;

import org.junit.Test;

public class GenericTest {

    @Test
    public void test2() {
        Bean> a = new Bean>() {
        };
        ParameterizedType type = (ParameterizedType) a.getClass().getGenericSuperclass();
        if (type.getActualTypeArguments() != null) {
            System.out.println(Arrays.asList(type.getActualTypeArguments()));
        }
    }

}

class Bean {

}

上述代碼亦可以輸出實(shí)際類型。

回到HttpResponse的第二parseAs方法的用法:Map ret = (Map)response.parseAs(new TypeToken>() {}.getType()),通過上面的分析,我們可以知道,TypeToken.getType()方法其實(shí)也是用來獲取泛型的實(shí)際類型的,這樣就可以將響應(yīng)反序列化為帶泛型的類型了。我們可以做如下實(shí)驗(yàn):

package org.hxb.spring.generic;

import java.lang.reflect.ParameterizedType;
import java.util.Map;

import org.junit.Test;

import com.google.common.reflect.TypeToken;

public class GenericTest {

    @Test
    public void test2() {
        Bean> a = new Bean>() {
        };
        ParameterizedType type = (ParameterizedType) a.getClass().getGenericSuperclass();
        if (type.getActualTypeArguments() != null) {
            System.out.println(type.getActualTypeArguments()[0]);
        }
    }
    @Test
    public void test3() {
        System.out.println(new TypeToken>() {}.getType());
    }

}

class Bean {

}
實(shí)際輸出:

  實(shí)驗(yàn)結(jié)果和我們猜想的那樣,我們?cè)倏纯碩ypeToken的無參構(gòu)造方法,


無參構(gòu)造方法的訪問權(quán)限是protected,有人會(huì)問了,那我怎么實(shí)例化?呵呵,其實(shí)作者的意圖就是為了確保你不能直接實(shí)例化TypeToken,但是我們可以用匿名實(shí)現(xiàn)類直接繼承TypeToken并實(shí)例化(就是多了對(duì)花括號(hào){})。
無參構(gòu)造方法調(diào)用了父類的capture(捕獲)方法,從截圖中可以看到,該方法調(diào)用了getGenericSuperclass,返回并且判斷父類的類型是不是ParameterizedType,不是的話便拋出異常,是就返回第一個(gè)。這也驗(yàn)證了我們的想法,其實(shí)parseAs方法就是用了上面的原理。
在很多反序列化的開源組件中,都用了這個(gè)原理例如com.fasterxml.jackson.databind.ObjectMapper.ObjectMapper 的readValue方法,所以我們會(huì)經(jīng)常見到實(shí)例化的時(shí)候會(huì)多個(gè)花括號(hào)。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/68802.html

相關(guān)文章

  • Java系列之泛型

    摘要:總結(jié)泛型的類型必須是引用類型,不能是基本類型,泛型的個(gè)數(shù)可以有多個(gè),可以使用對(duì)創(chuàng)建對(duì)象時(shí)的泛型類型以及方法參數(shù)類型進(jìn)行限制,如使用關(guān)鍵字和對(duì)泛型的具體類型進(jìn)行向下限制或向上限制,最后一點(diǎn),可以聲明泛型數(shù)組,但是不能創(chuàng)建泛型數(shù)組的實(shí)例。 自從 JDK 1.5 提供了泛型概念,泛型使得開發(fā)者可以定義較為安全的類型,不至于強(qiáng)制類型轉(zhuǎn)化時(shí)出現(xiàn)類型轉(zhuǎn)化異常,在沒有反省之前,可以通過 Object...

    MadPecker 評(píng)論0 收藏0
  • Java? 教程(類型擦除

    類型擦除 泛型被引入到Java語言中,以便在編譯時(shí)提供更嚴(yán)格的類型檢查并支持通用編程,為了實(shí)現(xiàn)泛型,Java編譯器將類型擦除應(yīng)用于: 如果類型參數(shù)是無界的,則用它們的邊界或Object替換泛型類型中的所有類型參數(shù),因此,生成的字節(jié)碼僅包含普通的類、接口和方法。 如有必要,插入類型轉(zhuǎn)換以保持類型安全。 生成橋接方法以保留擴(kuò)展泛型類型中的多態(tài)性。 類型擦除確保不為參數(shù)化類型創(chuàng)建新類,因此,泛型不會(huì)...

    zsy888 評(píng)論0 收藏0
  • Java知識(shí)點(diǎn)總結(jié)(Java泛型

    摘要:知識(shí)點(diǎn)總結(jié)泛型知識(shí)點(diǎn)總結(jié)泛型泛型泛型就是參數(shù)化類型適用于多種數(shù)據(jù)類型執(zhí)行相同的代碼泛型中的類型在使用時(shí)指定泛型歸根到底就是模版優(yōu)點(diǎn)使用泛型時(shí),在實(shí)際使用之前類型就已經(jīng)確定了,不需要強(qiáng)制類型轉(zhuǎn)換。 Java知識(shí)點(diǎn)總結(jié)(Java泛型) @(Java知識(shí)點(diǎn)總結(jié))[Java, Java泛型] [toc] 泛型 泛型就是參數(shù)化類型 適用于多種數(shù)據(jù)類型執(zhí)行相同的代碼 泛型中的類型在使用時(shí)指定 泛...

    linkin 評(píng)論0 收藏0
  • Java知識(shí)點(diǎn)總結(jié)(反射-反射操作泛型

    摘要:知識(shí)點(diǎn)總結(jié)反射反射操作泛型知識(shí)點(diǎn)總結(jié)反射采用泛型擦除的機(jī)制來引入泛型。中的泛型僅僅是給編譯器使用的,確保數(shù)據(jù)的安全性和免去強(qiáng)制類型轉(zhuǎn)換的麻煩。 Java知識(shí)點(diǎn)總結(jié)(反射-反射操作泛型) @(Java知識(shí)點(diǎn)總結(jié))[Java, 反射] Java采用泛型擦除的機(jī)制來引入泛型。Java中的泛型僅僅是給編譯器javac使用的, 確保數(shù)據(jù)的安全性和免去強(qiáng)制類型轉(zhuǎn)換的麻煩 。但是,__一旦編譯完成,...

    AprilJ 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<