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

Java如何獲取List<String>中的String詳解

 更新時(shí)間:2022年02月22日 09:28:28   作者:raysonxin  
工作了這么長時(shí)間了,一直沒有記錄的習(xí)慣,以至于導(dǎo)致我即便是查過的東西總會(huì)忘記,下面這篇文章主要給大家介紹了關(guān)于Java如何獲取List<String>中String的相關(guān)資料,需要的朋友可以參考下

前言

在寫這篇文章之前,我?guī)缀鯖]有思路去定義這個(gè)問題。只是知道,List<String>是泛型,是接口List<T>的實(shí)現(xiàn),實(shí)例化以后只能存儲(chǔ)String類型的對象,僅此而已!

提到泛型,每個(gè)Java開發(fā)人員都比較熟悉。常見的List、Map<K,V>等;另外,我們在進(jìn)行工具類、公共包的開發(fā)時(shí),也經(jīng)常使用泛型實(shí)現(xiàn)規(guī)范化、模板化的目標(biāo)。

問題場景

最近,在為新系統(tǒng)封裝公共包時(shí)遇到了一個(gè)與泛型有關(guān)的問題。在這里,結(jié)合實(shí)際場景討論一下。描述一下場景:封裝MQ中間件,統(tǒng)一MQ的消息訂閱與處理過程,統(tǒng)一MQ相關(guān)日志與監(jiān)控。

解決思路還是比較簡單的,整體由三個(gè)部分組成(如下圖所示):

  • IMessageSub:通過接口定義了需要訂閱的Topic、Tag、消費(fèi)組,并提供消息處理入口;
  • MessageQueueListener:實(shí)現(xiàn)了RocketMQ的接口MessageListenerConcurrently,是真正的消費(fèi)者,負(fù)責(zé)消息消費(fèi)處理。
  • MqSubManager:負(fù)責(zé)完成IMessageSub Bean的發(fā)現(xiàn)(所有實(shí)現(xiàn)類均為Spring的Bean對象),并通過MessageQueueListener實(shí)現(xiàn)對MQ的最終訂閱。

通過這個(gè)圖,我們可以知道MessageQueueListener#consumeMessage負(fù)責(zé)接收消息,轉(zhuǎn)換為指定類型后,交給IMessageSub#processMessage進(jìn)行處理。IMessageSub<T>是一個(gè)泛型接口,consumeMessage需要把消息轉(zhuǎn)換為IMessageSub實(shí)例的所需實(shí)際類型(如下ConcreteMessageSub1示例的DemoMsg)。

public interface IMessageSub<T> {
?String getTopic();
? ? String getTag();
? ? String getConsumerGroup();
? ? void processMessage(MqEvent<DemoMsg> mqEvent);
}

@Component
public class ConcreteMessageSub1 implements IMessageSub<DemoMsg> {
?public String getTopic(){
? ? ?return "TOPIC_TEST"
? ? }
? ??
?public String getTag(){
? ? ?return "TAG_TEST";
? ? }
?public String getConsumerGroup(){
? ? ?return "CID_TEST"
? ? }
?public void processMessage(MqEvent<DemoMsg> mqEvent){
? ? ?// do something...
? ? }
}

public class MessageQueueListener implements MessageListenerConcurrently {

? ? private IMessageSub<?> messageSub;
? ??
? ? public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
? ? ?
? ? }
}

每個(gè)MessageQueueListener對象持有一個(gè)IMessageSub的實(shí)例messageSub,可能是DemoMsg、DemoMsg2等等,如何才能確定對象MessageQueueListener#messageSub的實(shí)際類型呢?

問題討論

我們知道,對于一個(gè)非泛型對象,只需要調(diào)用其getClass()方法就可以了。但是對于泛型對象,做同樣的操作,結(jié)果卻不是我們想要的。如下簡單示例:

public static void main(String[] args) {
    String str = "";
    System.out.println(str.getClass().getName());
    
    List<String> list=new ArrayList<>();
    System.out.println(list.getClass().getName());
}

輸出結(jié)果:

java.lang.String
java.util.ArrayList

泛型對象list的輸出結(jié)果是“java.util.ArrayList”,貌似跟String沒有關(guān)系了,怎么回事兒呢?這就涉及到Java泛型的實(shí)現(xiàn)原理了。

Java的泛型是一種偽泛型,是通過類型擦除(Type Erasure)實(shí)現(xiàn)的參數(shù)化類型(Parameterized Type),也就是說把所操作的數(shù)據(jù)類型作為參數(shù)的一種語法。具體的歷史背景就是:

Java在實(shí)現(xiàn)泛型機(jī)制時(shí),為了避免新增泛型類型,直接把需要支持泛型的原始類型泛型化,比如:ArrayList變?yōu)锳rrayList。

這就需要,Java能夠?qū)崿F(xiàn)具備向前、向后兼容性的泛型,也就是說以前使用原始類型的代碼可以繼續(xù)被泛型使用,現(xiàn)在的泛型也可以作為參數(shù)傳遞給原始類型的代碼。

為了實(shí)現(xiàn)以上功能,Java 設(shè)計(jì)者將泛型完全作為了語法糖加入了新的語法中,也就是說泛型對于JVM來說是透明的,有泛型的和沒有泛型的代碼,通過編譯器編譯后所生成的二進(jìn)制代碼是完全相同的。編譯器在編譯過程中去除泛型的過程,被稱作類型擦除。

泛型的參數(shù)化類型本質(zhì)可以應(yīng)用在類、接口、方法,于是就產(chǎn)生了泛型類、泛型接口、泛型方法,可以說極大提升了Java代碼的靈活性。

考察大家一個(gè)小知識(shí),我們天天使用或者見到泛型,如List<E>,你知道它的各個(gè)組成部分叫什么名字嗎?

  • List<E>中的E稱為類型參數(shù)變量,整個(gè)稱為List<E>泛型類型。
  • List<Integer>中的Integer稱為實(shí)際類型參數(shù),整個(gè)List<Integer>稱為參數(shù)化的類型ParameterizedType。

類型擦除確實(shí)保證了良好的兼容性,但是在很多場景下我們確實(shí)需要知道泛型對象的原始信息。比如“問題場景”中獲取泛型接口實(shí)現(xiàn)類對象的實(shí)際類型參數(shù)。

雖然在編譯期間編譯器擦除了泛型,但是在字節(jié)碼中仍然保留了與泛型有關(guān)的信息,這就使得我們可以通過反射來獲取泛型擦除前的原始信息。為了表達(dá)泛型類型聲明,Java提供了接口Type及其子類型。

通過這些API我們可以對泛型的原始信息了如指掌:

  • Class類:描述具體類型,比較常見,可通過getClass()獲取。
  • TypeVariable接口:描述類型變量(如:T extends Comparable<? super T> ),通過getClass().getTypeParameters()。
  • WildcardType接口:描述通配符 (如:? super T )。
  • ParameterizedType接口:描述泛型類或接口類型(如:Comparable<? super T>),可通過getClass().getGenericInterfaces()獲取后篩選。
  • GenericArrayType接口:描述泛型數(shù)組(如:T[])

解決方案

了解泛型的原理后,結(jié)合反射包提供的API,我們就很容易解決第一部分提出的問題了。

結(jié)合上面的示例,MessageQueueListener#messageSub是一個(gè)泛型接口對象,實(shí)際為IMessageSub<T>泛型接口的實(shí)現(xiàn)類(如ConcreteMessageSub1),我們的目標(biāo)就是獲取ConcreteMessageSub1實(shí)現(xiàn)泛型接口時(shí)指定的實(shí)際類型參數(shù)DemoMsg。所以,需要按照以下步驟進(jìn)行處理:

  • 獲取對象messageSub所屬類(如:ConcreteMessageSub1)實(shí)現(xiàn)的接口列表;
  • 僅保留接口列表中的描述泛型接口的參數(shù)化類型對象(ParameterizedType);
  • 在參數(shù)化類型對象中獲取類型為IMessageSub的描述對象,即我們需要的參數(shù)化類型對象;
  • 獲取該參數(shù)化類型對象的第一個(gè)實(shí)際類型參數(shù)(接口聲明只有一個(gè)泛型參數(shù))。

用代碼實(shí)現(xiàn)如下所示:

/**
?* 獲取消息執(zhí)行器范性類型
?*
?* @return 類型
?*/
private Type getExecutorGenericType(IEventProcessor eventProcessor) {
? ? try {
? ? ? ? Optional<Type> typeOptional = Arrays.stream(eventProcessor.getClass().getGenericInterfaces())
? ? ? ? ? ? .filter(type -> ParameterizedType.class.isAssignableFrom(type.getClass()))
? ? ? ? ? ? .map(type -> (ParameterizedType)type)
? ? ? ? ? ? .filter(parameterizedType -> IEventProcessor.class.getTypeName().equals(parameterizedType.getRawType().getTypeName()))
? ? ? ? ? ? .map(ParameterizedType::getActualTypeArguments)
? ? ? ? ? ? .filter(actualTypes -> actualTypes.length > 0)
? ? ? ? ? ? .map(actualTypes -> actualTypes[0])
? ? ? ? ? ? .findFirst();
? ? ? ? return typeOptional.orElse(null);
? ? } catch (Throwable cause) {

? ? }
? ? return null;
}

本文總結(jié)

依托于泛型提供的API,我們可以開發(fā)出靈活的工具及框架,也可以使我們的代碼更加簡潔高效??梢哉f,Java的泛型是一種“語法糖”。以復(fù)用性更強(qiáng)的方式來提高開發(fā)效率,幫助開發(fā)人員在編譯階段識(shí)別系統(tǒng)存在的安全隱患,以更強(qiáng)的約束力來保證代碼的健壯性。

本來只想簡單的介紹獲取參數(shù)化類型的方式,可是當(dāng)把問題展開的時(shí)候,才發(fā)現(xiàn)自己對泛型的體系認(rèn)識(shí)不夠,每天上下班路上一邊學(xué)習(xí),一邊記錄筆記。關(guān)于泛型,還有許多要去學(xué)習(xí)和了解的知識(shí),大家一起進(jìn)步。

到此這篇關(guān)于Java如何獲取List&lt;String&gt;中的String的文章就介紹到這了,更多相關(guān)Java獲取List&lt;String&gt;中String內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot整合Redis實(shí)現(xiàn)高并發(fā)數(shù)據(jù)緩存的示例講解

    SpringBoot整合Redis實(shí)現(xiàn)高并發(fā)數(shù)據(jù)緩存的示例講解

    這篇文章主要介紹了SpringBoot整合Redis實(shí)現(xiàn)高并發(fā)數(shù)據(jù)緩存,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • Java網(wǎng)絡(luò)編程TCP實(shí)現(xiàn)文件上傳功能

    Java網(wǎng)絡(luò)編程TCP實(shí)現(xiàn)文件上傳功能

    這篇文章主要為大家詳細(xì)介紹了Java網(wǎng)絡(luò)編程TCP實(shí)現(xiàn)文件上傳功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-07-07
  • java開發(fā)微信公眾號(hào)支付

    java開發(fā)微信公眾號(hào)支付

    這篇文章主要給大家結(jié)合微信支付接口開發(fā)的實(shí)踐,從獲取用戶授權(quán)到各主要接口的使用方法等方面介紹微信支付的關(guān)鍵點(diǎn)技術(shù),有需要的小伙伴可以參考下
    2015-08-08
  • 淺談java中的移動(dòng)位運(yùn)算:,>>>

    淺談java中的移動(dòng)位運(yùn)算:,>>>

    這篇文章主要介紹了java中的移動(dòng)位運(yùn)算:,>>>文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • 完美解決idea無法搜索下載插件的問題

    完美解決idea無法搜索下載插件的問題

    這篇文章主要介紹了完美解決idea無法搜索下載插件的問題,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • Idea如何自定義VM配置

    Idea如何自定義VM配置

    這篇文章主要介紹了Idea如何自定義VM配置,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • Java StringBuilder的用法示例

    Java StringBuilder的用法示例

    這篇文章主要給大家介紹了關(guān)于Java StringBuilder用法的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Javaweb使用cors完成跨域ajax數(shù)據(jù)交互

    Javaweb使用cors完成跨域ajax數(shù)據(jù)交互

    本文由跨域、cors的概念開始,進(jìn)而向大家介紹了Javaweb使用cors完成跨域ajax數(shù)據(jù)交互的相關(guān)內(nèi)容,需要的朋友可以了解下。
    2017-09-09
  • JAVA中使用FTPClient實(shí)現(xiàn)文件上傳下載實(shí)例代碼

    JAVA中使用FTPClient實(shí)現(xiàn)文件上傳下載實(shí)例代碼

    本文給大家介紹如何利用jakarta commons中的FTPClient(在commons-net包中)實(shí)現(xiàn)上傳下載文件。非常不錯(cuò)具有參考借鑒價(jià)值,感興趣的朋友一起學(xué)習(xí)吧
    2016-06-06
  • 實(shí)例解析JAVA中代碼的加載順序

    實(shí)例解析JAVA中代碼的加載順序

    這篇文章主要介紹了舉例說明Java中代碼塊的執(zhí)行順序,需要的朋友可以參考下
    2017-04-04

最新評論