java:用Guava的TypeToken优雅处理通配符类型(WildcardType): ? extends Number

embedded/2025/2/21 7:41:39/

在日常开发中我们经常会遇到泛型和通配符类型WildcardType),比如当我们需要处理List<? extends Number>这样的类型时,如何优雅地创建这样的类型表示?本文将重点介绍如何通过Guava的TypeToken来实现通配符类型的构造,并结合开源项目中的实践进行深入解读。


一、通配符类型的痛点

在Java的泛型系统中,通配符类型(如<? extends T><? super T>)能提供更灵活的类型约束。但在反射场景中,我们无法直接通过new操作符或Class对象创建这种类型,这在需要动态生成类型的场合非常棘手。

考虑如下场景:

java">// 如何用代码表示List<? extends Number>类型?
Type listType = new ParameterizedType() {public Type[] getActualTypeArguments() {return new Type[] { /* 如何表示? extends Number */ };}// ...
}

我们希望能有一个工具来优雅地构造这样的通配符类型


WildcardTyper_24">二、WildcardTyper工具类解析

以下是一个专为通配符类型设计的工具类,来自真实项目代码:

java">public abstract class WildcardTyper<T> {public final WildcardType wildcardType;protected WildcardTyper(boolean subType) {TypeToken<?> token = subType ? new TypeToken<Class<? extends T>>() {} : new TypeToken<Class<? super T>>() {};this.wildcardType = (WildcardType) ((ParameterizedType) token.getType()).getActualTypeArguments()[0];}// 子类型快捷方式public abstract static class SubOf<T> extends WildcardTyper<T> {public SubOf() { super(true); }}// 超类型快捷方式public abstract static class SuperOf<T> extends WildcardTyper<T> {public SuperOf() { super(false); }}
}

核心原理:

  1. TypeToken魔法:利用Guava的TypeToken捕获泛型参数类型信息
  2. 参数类型提取:构造如Class<? extends T>的参数化类型,从中提取通配符类型
  3. 子类化技巧:通过继承时的具体类型声明保留泛型参数信息

三、实战使用示例

示例1:创建基础通配符类型
java">// ? extends Number
WildcardType extendsNumber = new WildcardTyper.SubOf<Number>() {}.wildcardType;// ? super Integer
WildcardType superInteger = new WildcardTyper.SuperOf<Integer>() {}.wildcardType;
示例2:复杂容器类型构建
java">// List<? extends Number>
Type listOfNumbers = new ParameterizedTypeImpl(List.class, new Type[] { new WildcardTyper.SubOf<Number>() {}.wildcardType }
);// Map<String, ? super Date>
Type mapWithSuper = new ParameterizedTypeImpl(Map.class,new Type[] { String.class, new WildcardTyper.SuperOf<Date>() {}.wildcardType }
);
示例3:类型系统验证
java">@Test
public void testTypeCompatibility() {// 验证Float可以赋值给? extends NumberWildcardType numType = new WildcardTyper.SubOf<Number>() {}.wildcardType;assertTrue(TypeToken.of(numType).isSupertypeOf(Float.class));// 验证List<? extends Number>能接受ArrayList<Double>ParameterizedType listType = new ParameterizedTypeImpl(List.class, new Type[] { numType });assertTrue(TypeToken.of(listType).isSupertypeOf(ArrayList.class));
}

四、高级技巧:类型转换中的通配符处理

在BaseTypeTransformer的代码中,我们看到了这样的高级应用:

java">// 定义从Date到子类型的转换器
transTable.put(Date.class, new WildcardTyper.SubOf<Date>() {}.wildcardType, new Date2SubTransformer()
);// 转换器实现
class Date2SubTransformer extends BaseFunction<Date, Date> {public Date doApply(Date input) {return (Date) outputType.getRawType().getConstructor(long.class).newInstance(input.getTime());}
}

工作原理:

  1. 通过SubOf<Date>声明目标类型为? extends Date
  2. 运行时动态确定实际类型(如java.sql.Date
  3. 反射调用具体的构造函数实例化

BaseTypeTransformer的完整代码:

common-base2/src/main/java/com/gitee/l0km/com4j/basex/BaseTypeTransformer.java · 10km/common-java - 码云 - 开源中国


五、为何不用静态工厂方法?

观察WildcardTyper的设计,可能有读者会问:为什么不像这样提供静态方法?

java">// 期望的API(但这行不通!)
WildcardType type = WildcardTyper.createWildcard(Number.class, true);

根本原因在于类型擦除:静态方法无法捕获泛型参数的具体类型信息。而通过抽象类的继承模式:

  1. 保留了完整的泛型类型信息
  2. 通过getClass().getGenericSuperclass()获取类型参数
  3. 使TypeToken能准确推断出通配符的边界

六、适用场景分析

  1. 泛型反射操作:在需要解析或生成泛型类型的反射场景
  2. 类型转换系统:构建灵活的类型转换体系
  3. 序列化/反序列化:处理带有通配符的复杂泛型类型
  4. DI容器实现:解析依赖注入时的泛型限定

七、总结与最佳实践

最佳实践建议:

  1. 尽量使用子类模式:如new SubOf<Number>() {}保持类型安全
  2. 结合TypeToken使用:借助Guava的强大类型推断能力
  3. 注意类型边界:明确理解extendssuper的行为差异
  4. 防御式编程:在反射访问时做好类型校验

性能考虑TypeToken的内部缓存机制保证了重复使用的性能,但在高频场景建议缓存生成的WildcardType实例。

通过本文的代码示例和原理分析,相信读者已经掌握了使用TypeToken优雅处理通配符类型的诀窍。这个技巧在处理复杂泛型系统时显示出极大的威力,值得加入每个Java开发者的工具箱。

WildcardTyper__179">八、WildcardTyper 完整代码

java">package com.gitee.l0km.com4j.basex.reflection;import java.lang.reflect.ParameterizedType;
import java.lang.reflect.WildcardType;import com.google.common.reflect.TypeToken;/*** 用于生成通配符类型WildcardType)的抽象工具类,支持上界和下界通配符类型<br>* 示例:* <pre>* WildcardTyper<Number> superWildcardTyper = new WildcardTyper<Number>(false) {};* superWildcardTyper.wildcardType ==> ? super Number* WildcardTyper<Number> extendsWildcardTyper = new WildcardTyper<Number>(true) {};* extendsWildcardTyper.wildcardType ==> ? extends Number* </pre>* @param <T> 通配符类型中的边界类型参数,决定通配符类型的上下界基础类型* @author guyadong* @since 4.4.0*/
public abstract  class WildcardTyper<T> {/** 生成的通配符类型实例,如:? super Number */public final WildcardType wildcardType;/** 延迟初始化的TypeToken实例,线程安全 */private volatile TypeToken<?> token;/*** 构造方法,根据subType参数生成对应的通配符类型* * @param subType 通配符类型方向标识:*               - true:生成上界通配符类型(? extends T)*               - false:生成下界通配符类型(? super T)*/@SuppressWarnings("serial")protected WildcardTyper(boolean subType){TypeToken<?> _token;// 根据subType选择创建不同通配符类型TypeTokenif(subType) {// 创建上界通配符类型TypeToken(Class<? extends T>)_token = new TypeToken<Class<? extends T>>(getClass()) {};}else {// 创建下界通配符类型TypeToken(Class<? super T>)_token = new TypeToken<Class<? super T>>(getClass()) {};}// 从ParameterizedType中提取实际的通配符类型参数this.wildcardType = (WildcardType) ((ParameterizedType) _token.getType()).getActualTypeArguments()[0];}/*** 获取与wildcardType对应的TypeToken实例* * @return 延迟初始化的TypeToken对象,保证线程安全的单例模式*/public TypeToken<?> getToken() {TypeToken<?> _token = this.token;if(null == _token){// 使用双重检查锁定保证线程安全synchronized(wildcardType){_token = this.token;if(null == _token){this.token = _token = TypeToken.of(wildcardType);}}}return _token;}/*** 用于生成下界通配符类型 (? super T) 的抽象基类*/public abstract static class SuperOf<T> extends WildcardTyper<T> {protected SuperOf() {super(false); // 固定调用父类下界构造}}/*** 用于生成上界通配符类型 (? extends T) 的抽象基类 */public abstract static class SubOf<T> extends WildcardTyper<T> {protected SubOf() {super(true); // 固定调用父类上界构造}}
}

BaseTypeTransformer完整代码:common-base2/src/main/java/com/gitee/l0km/com4j/basex/BaseTypeTransformer.java · 10km/common-java - 码云 - 开源中国

WildcardTyperT的单元测试代码:common-base2/src/test/java/com/gitee/l0km/com4j/basex/reflection/WildcardTyperTest.java · 10km/common-java - 码云 - 开源中国


九、参考资料

  1. Guava官方文档
    Guava GitHub仓库
    TypeToken用法详解

  2. Java泛型官方教程
    Oracle Java泛型教程
    Java类型擦除机制

  3. 反射与通配符类型
    Java WildcardType API文档
    ParameterizedType深入解析

  4. 实例项目参考
    BaseTypeTransformer完整源码
    WildcardTyper实现细节

  5. 扩展阅读
    Java类型系统科普
    泛型类型安全最佳实践


希望这些资料能帮助你深入理解Java泛型和反射的奥秘!如果有其他技术问题,欢迎随时交流探讨。 💡


http://www.ppmy.cn/embedded/163511.html

相关文章

自有证书的rancher集群使用rke部署k8s集群异常

rancher使用自签域名或者商业证书容易踩到的坑。 最开始的报错&#xff1a; docker logs kubelet‘s id E0214 13:04:14.590268 9614 pod_workers.go:1300] "Error syncing pod, skipping" err"failed to \"StartContainer\" for \"clust…

实现LED流水灯的几种方法

1.实现原理 通过不断给P1中不同的IO口置低电平&#xff0c;从而达到LED流水灯的效果。 2.程序思路 方法一&#xff1a;通过给P1口赋不同的值从而达到流水灯的效果 /* 头文件声明区域 */ #include <REGX52.H>/* 延时函数 */ //需要记&#xff01;&#xff01;&#…

根据CAN通讯矩阵使用CANoe生成DBC

目录 1 DBC文件作用2 根据CAN通讯矩阵表使用CANoe建立DBC文件3 总结 1 DBC文件作用 DBC&#xff08;DataBase CAN&#xff09;文件是一种流转与各类开发测试软件的交互性文件&#xff0c;其内容根据CAN通讯矩阵表建立&#xff0c;用于解释描述can通讯报文 2 根据CAN通讯矩阵表…

DeepSeek 助力 Vue 开发:打造丝滑的颜色选择器(Color Picker)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

筛选相同项

# import os # import pandas as pd# # 文件路径&#xff0c;根据实际情况修改 # file_path_1 rC:\Users\Administrator\Desktop\python\文件1.xlsx # file_path_2 rC:\Users\Administrator\Desktop\python\文件2.xlsximport os import pandas as pd# 获取当前脚本所在的目录…

opencv实时二维码识别的一种实现与思路分享

在嵌入式平台上比如 rk3568 这种弱鸡的平台,要做到实时视频处理就非常鸡肋,不像英伟达那种 deepstrem 什么的。 开始的时候,我们使用python 下的 pyzbar + opencv opencv 读取摄像头的数据然后每帧送到 pyzbar 二维码识别函数里面进行处理,然后打印出识别的数字。结果,非常…

Python实现语音识别详细教程【2025】最新教程

文章目录 前言一、环境搭建1. 下载 Python2. 安装 Python3 使用 pip 安装必要的库 二、使用 SpeechRecognition 库进行语音识别1.识别本地音频文件2.实时语音识别3. 使用其他语音识别引擎 注意事项 前言 以下是一份较为完整的 Python 语音识别教程&#xff0c;涵盖环境搭建、使…

Android - Handler使用post之后,Runnable没有执行

问题&#xff1a;子线程创建的Handler。如果 post 之后&#xff0c;在Handler.removeCallbacks(run)移除了&#xff0c;下次再使用Handler.postDelayed(Runnable)接口或者使用post时&#xff0c;Runnable是没有执行。导致没有收到消息。 解决办法&#xff1a;只有主线程创建的…