一起來分析Java泛型和泛型的通配符
本篇文章為大家帶來了關於java的相關知識,其中主要介紹了關於泛型以及泛型的通配符相關問題,因為泛型的支持是編譯器支持,字節碼載入到虛擬機器的時候泛型資訊已經被擦除,所以泛型不支援一些運行時特性,下面一起來看一下,希望對大家有幫助。
推薦學習:《java影片教學》
泛型不是執行時間特性
我們這裡依然說的是Open JDK
因為泛型的支持是編譯器支持,字節碼加載到虛擬機的時候泛型信息已經被擦除,所以泛型不支持一些運行時特性。所以要注意有些寫法將編譯不過,例如new。
如下,類別Plate
new Plate(...) new Plate<T>(...) class Plate<T> { T item; public Plate(T t) { new T();//是错误的,因为T是一个不被虚拟机所识别的类型,最终会被编译器擦除转为Object类给到虚拟机 item = t; } public void set(T t) { item = t; } public T get() { return item; } }
泛型T不能被new,因為T是一個不被虛擬機器所識別的類型。
泛型通配符
存在三種形式的用通配符的泛型變數表達,分別是:
extends A>: C extends A> c,c中的元素型別都是A或A的子類別
super B>:C super B> c,c中的元素型別是B或B的父類別
>:C> c,c中的元素型別不確定
##具體是什麼意思以及怎麼使用,我們一起來看看吧~
上界通配符
在物件導向程式設計領域,我們認為基底類別base在最上層。從繼承樹的角度來看,Object類別處於最上層。
所以我們將這樣的表達 extends T>稱為上界通配符。
extends T>表示T或繼承T型別的任意泛型類型。
先看下面這個範例.Sping Webmvc中的RequestBodyAdvice
public interface RequestBodyAdvice { /** * Invoked first to determine if this interceptor applies. * @param methodParameter the method parameter * @param targetType the target type, not necessarily the same as the method * parameter type, e.g. for {@code HttpEntity<String>}. * @param converterType the selected converter type * @return whether this interceptor should be invoked or not */ boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType); ... }
在ping Webmvc中,RequestBodyAdvice用來處理http請求的body,supports用來判斷是否支援某種參數類型到HttpMessage請求的轉換。
HttpMessageConverter是一個接口,比如支持Body為Json格式的JsonViewRequestBodyAdvice類,實現如下:
@Override public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return (AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType) && methodParameter.getParameterAnnotation(JsonView.class) != null); }
使用AbstractJackson2HttpMessageConverter來處理JsonView,Jackson2庫是流行的Java JSON解析庫之一,也是Springboot自帶的HttpMessageConverter.
不同的使用方可以自己定義不同類型的Advice,便使得能支援非常多的參數類型比如xml,那麼sping-webmvc的功能也就更加靈活通用了,可以將許多Type透過不同的HttpMessageConverter翻譯為不同的HttpInputMessage請求。如下所示,
@Override public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException { for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) { if (advice.supports(parameter, targetType, converterType)) { request = advice.beforeBodyRead(request, parameter, targetType, converterType); } } return request; }
透過getMatchingAdvice(parameter, RequestBodyAdvice.class)獲得匹配的advice列表,遍歷這個列表解析支援parameter的Advice得到HttpInputMessage類型的請求。 上界通配符的表達無法再set
#使用上屆通配符的表達方式無法再設定泛型欄位,其實意思就是上界通配符不能改變已經設定的泛型類型,我們一起來看這個demo。
@Test void genericTest() { Plate<Apple> p = new Plate<Apple>(new Apple()); p.set(new Apple());//可以set Apple apple = p.get(); Plate<? extends Fruit> q = new Plate<Apple>(new Apple()); Fruit fruit = q.get(); q.set(new Fruit());//将编译错误 }
Plate extends Fruit>這種表達方式意味著java編譯期只知道容器裡面存放的是Fruit和它的派生類,具體是什麼類型不知道,可能是Fruit、Apple或者其他子類, 編譯器在p賦值以後,盤子裡面沒有標記為“Apple",只是標記了一個佔位符“CAP#1”(可以通過javap反編譯字節碼來嚴重),來表示捕獲一個Fruit或者Fruit的子類別。
但是不管是不是通配符的寫法,泛型終究指的是一種具體的類型,而且被編譯器使用了特殊的“CAP#1”,所以我們無法再重新設定這個字段了,否則就會出現類型不一致的編譯錯誤了。
但這個特點對用法來說並沒有妨礙,框架使用上界通配符範式達到靈活擴展的目的。
下界通配符
接下來我們一起看下下界通配符, super T>表示T或T父類別的任意型,下界的型別是T。 語言陷阱
我們在理解上容易掉入一個陷阱,以為只可以設定Fruit或Fruit的基底類別。實際上Fruit和Fruit的子類別才可以設定進去,讓我們寫一個單元測試來看看。
@Test void genericSuperTest() { Plate<? super Fruit> p = new Plate<Fruit>(new Fruit()); p.set(new Apple()); //ok,存取的时候可以存任意可以转为T的类或T p.set(new Object()); //not ok,无法 set Object Object object = p.get();//ok Fruit object = p.get();//not ok,super Fruit不是Fruit的子类 }
訪問的時候可以存可以轉為T的類別或T,也就是可以設定Fruit或Fruit子類別的類別。
但是使用的時候必須使用object來引用。 spring-kafka的非同步回呼
####現在,讓我們來看實際的一個例子。 ###SettableListenableFuture是spring 并发框架的一个类,继承自Future
在spring-kafka包里使用了SettableListenableFuture来设置异步回调的结果,kafka客户端调用 doSend发送消息到kafka队列之后,我们可以异步的判断是否发送成功。
public class SettableListenableFuture<T> implements ListenableFuture<T> { ... @Override public void addCallback(ListenableFutureCallback<? super T> callback) { this.settableTask.addCallback(callback); } @Override public void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback) { this.settableTask.addCallback(successCallback, failureCallback); } ...
SettableListenableFuture有重载的addCallback函数,支持添加ListenableFutureCallback super T> callback和SuccessCallback super T> successCallback;当调用的异步方法成功结束的时候使用notifySuccess来触发onSuccess的执行,这个时候将实际异步执行的结果变成参数给callback调用。
private void notifySuccess(SuccessCallback<? super T> callback) { try { callback.onSuccess((T) this.result); } catch (Throwable ex) { // Ignore } }
SuccessCallback是一个函数式接口,从设计模式的角度来看是一个消费者,消费
public interface SuccessCallback<T> { /** * Called when the {@link ListenableFuture} completes with success. * <p>Note that Exceptions raised by this method are ignored. * @param result the result */ void onSuccess(@Nullable T result); }
为什么要用notifySuccess(SuccessCallback super T> callback)呢?
这是因为super能支持的范围更多,虽然实际产生了某一个具体类型的结果,比如kafka的send函数产生的结果类型为SendResult,其他的客户端可能使用其他的Result类型,但是不管是什么类型,我们在使用Spring的时候,可以对异步的结果统一使用Object来处理。
比如下面的这段代码,虽然是针对kafka客户端的。但对于其他的使用了Spring SettableListenableFuture的客户端,我们也可以在addCallback函数里使用Object来统一处理异常。
@SneakyThrows public int kafkaSendAndCallback(IMessage message) { String msg = new ObjectMapper().writeValueAsString(message); log.debug("msg is {}. ", msg); ListenableFuture send = kafkaTemplate.send("test", msg); addCallback(message, send); return 0; } private void addCallback(IMessage msg, ListenableFuture<SendResult<String, String>> listenableFuture) { listenableFuture.addCallback( new SuccessCallback<Object>() { @Override public void onSuccess(Object o) { log.info("success send object = " + msg.getContentType() + msg.getId()); } }, new FailureCallback() { @Override public void onFailure(Throwable throwable) { log.error("{}发送到kafka异常", msg.getContentType() + msg.getId(), throwable.getCause()); } }); } }
声明某个条件的任意类型?
比如 Collection
@Override public boolean removeAll(Collection<?> collection) { return delegate().removeAll(collection); }
Collection的removeAll函数移除原集合中的一些元素,因为最终使用equals函数比较要移除的元素是否在集合内,所以这个元素的类型并不在意。
我们再看一个例子,LoggerFactory
public class LoggerFactory { public static Logger getLogger(Class<?> clazz) { return new Logger(clazz.getName()); } }
LoggerFactory可以为任意Class根据它的名字生成一个实例。
总结:设计模式PECS
PECS是producer extends consumer super的缩写。
也是对我们上面的分析的一个总结
意思是extends用于生产者模式,而super用于消费者模式。
消费者模式:比如上面的callback结果是为了消费;这些结果被消费处理。
生产者模式:比如那些Converter,我们要处理特定格式的http请求,需要生产不同的转换器Converter。
推荐学习:《java视频教程》
以上是一起來分析Java泛型和泛型的通配符的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip

PHP成為許多網站首選技術棧的原因包括其易用性、強大社區支持和廣泛應用。 1)易於學習和使用,適合初學者。 2)擁有龐大的開發者社區,資源豐富。 3)廣泛應用於WordPress、Drupal等平台。 4)與Web服務器緊密集成,簡化開發部署。

PHP適用於Web開發和內容管理系統,Python適合數據科學、機器學習和自動化腳本。 1.PHP在構建快速、可擴展的網站和應用程序方面表現出色,常用於WordPress等CMS。 2.Python在數據科學和機器學習領域表現卓越,擁有豐富的庫如NumPy和TensorFlow。
