您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

一起来分析Java泛型和泛型的通配符

2024/4/10 10:20:41发布8次查看
本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于泛型以及泛型的通配符相关问题,因为泛型的支持是编译器支持,字节码加载到虚拟机的时候泛型信息已经被擦除,所以泛型不支持一些运行时特性,下面一起来看一下,希望对大家有帮助。
推荐学习:《java视频教程》
泛型不是运行时特性我们这里依然说的是open jdk
因为泛型的支持是编译器支持,字节码加载到虚拟机的时候泛型信息已经被擦除,所以泛型不支持一些运行时特性。所以要注意有些写法将编译不过,比如new。
如下,类plate8742468051c85b06f0a0af9e3e506b5c是带泛型的类,如下演示,
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类,实现如下:
@overridepublic 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请求。如下所示,
@overridepublic 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的子类才可以设置进去,让我们写一个单元测试来看看。
@testvoid 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<t>,我们知道future表示异步执行的结果,t表示返回结果的类型。listenablefuture可以支持设置回调函数,如果成功了怎么处理,如果异常又如何处理。
在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是一个函数式接口,从设计模式的角度来看是一个消费者,消费<t>类型的result。listenablefuturecallback同理。
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<e>类的这个函数,
@overridepublic 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根据它的名字生成一个实例。
总结:设计模式pecspecs是producer extends consumer super的缩写。
也是对我们上面的分析的一个总结
意思是extends用于生产者模式,而super用于消费者模式。
消费者模式:比如上面的callback结果是为了消费;这些结果被消费处理。
生产者模式:比如那些converter,我们要处理特定格式的http请求,需要生产不同的转换器converter。
推荐学习:《java视频教程》
以上就是一起来分析java泛型和泛型的通配符的详细内容。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product