当前位置: 首页 > news >正文

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

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


一、通配符类型的痛点

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

考虑如下场景:

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

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


二、WildcardTyper工具类解析

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

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:创建基础通配符类型
// ? extends Number
WildcardType extendsNumber = new WildcardTyper.SubOf<Number>() {}.wildcardType;

// ? super Integer
WildcardType superInteger = new WildcardTyper.SuperOf<Integer>() {}.wildcardType;
示例2:复杂容器类型构建
// 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:类型系统验证
@Test
public void testTypeCompatibility() {
    // 验证Float可以赋值给? extends Number
    WildcardType 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的代码中,我们看到了这样的高级应用:

// 定义从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的设计,可能有读者会问:为什么不像这样提供静态方法?

// 期望的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 完整代码

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选择创建不同通配符类型的TypeToken
		if(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泛型和反射的奥秘!如果有其他技术问题,欢迎随时交流探讨。 💡

相关文章:

  • JSON类型理解(前后端交互/内存对数据操作)
  • 【论文技巧】Mermaid VSCode插件制作流程图保存方法
  • POI优化Excel录入
  • LangFlow和LangChain有什么区别
  • 大数据SQL调优专题——Flink执行原理
  • Web3 通识
  • 解锁外观模式:Java 编程中的优雅架构之道
  • TortoiseSVN\bin下的没有svn.exe的解决问题
  • Linux的基础指令和环境部署,项目部署实战(下)
  • pyinstaller打包报错:INTEL MKL ERROR: 找不到指定的模块。 mkl_intel_thread.dll.
  • linux 安装启动zookeeper全过程及遇到的坑
  • C++ 模拟真人鼠标轨迹算法 - 防止游戏检测
  • 启元世界(Inspir.ai)技术浅析(七):AI Beings 平台
  • Nginx下proxy_redirect的三种配置方式
  • 【系列教程】Python第四课:条件判断 | 让程序学会思考的秘密
  • UE求职Demo开发日志#33、34 优化#2 删没用的场景和优化UI
  • 浅谈模组-相机鬼像
  • blender笔记2
  • Leetcode 3453. Separate Squares I
  • MISRA C vs CERT C 是两种广泛使用的 C 语言编码规范对比介绍
  • 上海市政府党组会议传达学习习近平总书记重要讲话精神,部署抓好学习贯彻落实
  • 中小企业数字化转型的破局之道何在?
  • 贵州省黔西市发生载人游船侧翻事故
  • 今晚上海地铁1、2、10、17号线加开定点班车至次日0点
  • 文旅局局长回应游客住家里:“作为一个宣恩市民我也会这么做”
  • 香港金紫荆广场举行五四升旗礼