0

0

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

WBOY

WBOY

发布时间:2022-10-10 15:55:55

|

2253人浏览过

|

来源于掘金

转载

本篇文章给大家带来了关于java的相关知识,其中主要介绍了关于泛型以及泛型的通配符相关问题,因为泛型的支持是编译器支持,字节码加载到虚拟机的时候泛型信息已经被擦除,所以泛型不支持一些运行时特性,下面一起来看一下,希望对大家有帮助。

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

推荐学习:《java视频教程

泛型不是运行时特性

我们这里依然说的是Open JDK

因为泛型的支持是编译器支持,字节码加载到虚拟机的时候泛型信息已经被擦除,所以泛型不支持一些运行时特性。所以要注意有些写法将编译不过,比如new。

立即学习Java免费学习笔记(深入)”;

如下,类Plate是带泛型的类,如下演示,

new Plate(...)
new Plate(...)
class Plate {
    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}.
    * @param converterType the selected converter type
    * @return whether this interceptor should be invoked or not
    */
   boolean supports(MethodParameter methodParameter, Type targetType,
         Class> converterType);
   ...
}

在ping Webmvc中,RequestBodyAdvice用来处理http请求的body,supports用来判断是否支持某种参数类型到HttpMessage请求的转换。

HttpMessageConverter是一个接口,比如支持Body为Json格式的JsonViewRequestBodyAdvice类,实现如下:

@Override
public boolean supports(MethodParameter methodParameter, Type targetType,
      Class> 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> 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 p = new Plate(new Apple());
        p.set(new Apple());//可以set
          Apple apple = p.get();
          
        Plate q = new Plate(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”,所以我们无法再重新设置这个字段了,否则就会出现类型不一致的编译错误了。

Android数据格式解析对象JSON用法 WORD版
Android数据格式解析对象JSON用法 WORD版

本文档主要讲述的是Android数据格式解析对象JSON用法;JSON可以将Java对象转成json格式的字符串,可以将json字符串转换成Java。比XML更轻量级,Json使用起来比较轻便和简单。JSON数据格式,在Android中被广泛运用于客户端和服务器通信,在网络数据传输与解析时非常方便。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看

下载

但这个特点对于用法来说并没有妨碍,框架使用上界通配符范型达到灵活扩展的目的。

下界通配符

接下来我们一起看下下界通配符, super T>表示T或T父类的任意类型,下界的类型是T。

语言陷阱

我们在理解上容易掉入一个陷阱,以为只可以设置Fruit或Fruit的基类。实际上Fruit和Fruit的子类才可以设置进去,让我们写一个单元测试来看看。

@Test
void genericSuperTest() {
    Plate p = new Plate(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,我们知道Future表示异步执行的结果,T表示返回结果的类型。ListenableFuture可以支持设置回调函数,如果成功了怎么处理,如果异常又如何处理。

在spring-kafka包里使用了SettableListenableFuture来设置异步回调的结果,kafka客户端调用 doSend发送消息到kafka队列之后,我们可以异步的判断是否发送成功。

public class SettableListenableFuture implements ListenableFuture {
  ...
   @Override
   public void addCallback(ListenableFutureCallback callback) {
      this.settableTask.addCallback(callback);
   }
   @Override
   public void addCallback(SuccessCallback 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 callback) {
   try {
      callback.onSuccess((T) this.result);
   }
   catch (Throwable ex) {
      // Ignore
   }
}

SuccessCallback是一个函数式接口,从设计模式的角度来看是一个消费者,消费类型的result。ListenableFutureCallback同理。

public interface SuccessCallback {
   /**
    * Called when the {@link ListenableFuture} completes with success.
    * 

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> listenableFuture) {
        listenableFuture.addCallback(
                new SuccessCallback() {
                    @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速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

866

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

745

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

741

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

420

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

447

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

431

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16948

2023.08.03

拼多多赚钱的5种方法 拼多多赚钱的5种方法
拼多多赚钱的5种方法 拼多多赚钱的5种方法

在拼多多上赚钱主要可以通过无货源模式一件代发、精细化运营特色店铺、参与官方高流量活动、利用拼团机制社交裂变,以及成为多多进宝推广员这5种方法实现。核心策略在于通过低成本、高效率的供应链管理与营销,利用平台社交电商红利实现盈利。

12

2026.01.26

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.9万人学习

C# 教程
C# 教程

共94课时 | 7.6万人学习

Java 教程
Java 教程

共578课时 | 51.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号