This article brings you relevant knowledge about java, which mainly introduces issues related to generics and generic wildcards, because the support of generics is compiler support, byte Generic information has been erased when the code is loaded into the virtual machine, so generics do not support some runtime features. Let's take a look at them together. I hope it will be helpful to everyone.
Recommended study: "java video tutorial"
We still have it here What I’m talking about is Open JDK
Because generic support is supported by the compiler, the generic information has been erased when the bytecode is loaded into the virtual machine, so generics do not support some runtime features. So be aware that some writing methods will not compile, such as new.
As follows, the class 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; } }
The generic T cannot be new, because T is a type that is not recognized by the virtual machine.
There are three forms of generic variable expression using wildcard characters, which are:
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);
...
}
@Override public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) { return (AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType) && methodParameter.getParameterAnnotation(JsonView.class) != null); }
@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; }
The expression using the previous wildcard can no longer set the generic field. In fact, it means that the upper bound wildcard is no longer set. Wildcards cannot change the set generic type. Let's take a look at this 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());//将编译错误 }
We easily fall into a trap in understanding, thinking that we can only set Fruit or the base class of Fruit. In fact, only Fruit and Fruit subclasses can be set in. Let's write a unit test to see.
@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的子类 }
Now, let’s look at a practical example.
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是producer extends consumer super的缩写。
也是对我们上面的分析的一个总结
意思是extends用于生产者模式,而super用于消费者模式。
消费者模式:比如上面的callback结果是为了消费;这些结果被消费处理。
生产者模式:比如那些Converter,我们要处理特定格式的http请求,需要生产不同的转换器Converter。
推荐学习:《java视频教程》
The above is the detailed content of Let's analyze Java generics and generic wildcards together. For more information, please follow other related articles on the PHP Chinese website!