【API知识】类型转换工具ConvertUtils引发的思考
前言
在读取Excel文件数据,有时候不可避免地需要把获取到的字符串转型为基本类型的对象。以前都是自己写转换,难度也不大。后来听说,有可以直接用的轮子——Apache 的commons-beanutils这个包,有提供ConvertUtils。以下我的相关记录。
我要的异常呢?
听说有可以用的轮子,第一反应,肯定是拿来先跑几个测试案例。这东西使用起来也很简单,参数就是源字符串和目的类型。我就先测了一下,一个乱码String转Boolean,看看会不会抛出异常。结果出乎意外的是,它居然没有抛出异常,还返回了false。认真看了一下,才发现它直接把异常打印出来了,还返回了默认值。
代码如下:
public class ConvertTest { public static void main(String[] args) { Object result; result = ConvertUtils.convert("@7##jF*&%#$", Boolean.class); System.out.println(result); } }
输出结果:
真是够呛,你不抛出异常,我留你何用。我就是要你抛出异常,然后我再在上层决定怎么和用户交互,你倒好,直接打印出来了,还给我个默认值,那怎么知道原来的值到底是错误的还是bool false。
于是我想,这种工具都有一个尿性——可配置。我就想肯定有什么方法,比如xxxwithException(),或者throwException()这样的设定。结果翻了一下,还真没有。哇,上头。
内核——ConvertUtilsBean
刚好有时间,于是就看它到底怎么实现的。看了才知道,原来它是把工作委托给ConvertUtilsBean来做的。
public static Object convert(final String value, final Class<?> clazz) { //这里获取一个ConvertUtilsBean的实例来执行convert方法 return ConvertUtilsBean.getInstance().convert(value, clazz); }
于是顺藤摸瓜,我就随便翻了翻ConvertUtilsBean。我的眼睛就盯着,看看有没有exception这个关键字,果然让我找到了!!!
public void register(final boolean throwException, final boolean defaultNull, final int defaultArraySize) { registerPrimitives(throwException); registerStandard(throwException, defaultNull); registerOther(throwException); registerArrays(throwException, defaultArraySize); }
这里可以配置是否要抛出异常。于是我就用这个bean,测了一下,果然可以。
public class ConvertBeanTest { public static void main(String[] args) { ConvertUtilsBean convertUtilsBean = new ConvertUtilsBean(); convertUtilsBean.register(true, false, 0); Object obj; obj = convertUtilsBean.convert("@7##jF*&%#$", Boolean.class); System.out.println(obj); } }
输出:
很好,终于抛出异常了。那么,我用这个ConvertUtilsBean就可以了。
ConvertUtilsBean为何能够想转什么就转什么?
实际上,它也不是想转什么就转什么。初始条件下,它内部只注册了基本类型的转换器。
public ConvertUtilsBean() { converters.setFast(false); //这个方法是关键,它清除当前所有转换器,并重新初始化 deregister(); converters.setFast(true); }
public void deregister() { converters.clear(); //false参数表示,是否抛出异常。即默认不抛出异常。 //这里注册了基本类型的转换器 registerPrimitives(false); registerStandard(false, false); registerOther(true); registerArrays(false, 0); register(BigDecimal.class, new BigDecimalConverter()); register(BigInteger.class, new BigIntegerConverter()); }
注册是什么概念?
“注册”这个词,看上去挺玄乎的,其实一般就是写到一个注册表里面,然后需要的时候从表中检索。在这个实现中,注册表,不过就是一张HashMap。而注册操作,就是把Converter对象放到这个哈希表中。
/** * The set of {@link Converter}s that can be used to convert Strings * into objects of a specified Class, keyed by the destination Class. */ private final WeakFastHashMap<Class<?>, Converter> converters = new WeakFastHashMap<Class<?>, Converter>();
我们可以看到,这个表的Key是类型。也就是说,我们在使用convert方法的时候,已经指定了key,自然就找到了对应的Converter。那我们还能想到什么呢,那就是“覆盖”。因为HashMap中,Key是唯一的,所以同种类型的Converter只能存在一个,即新注册的Converter会覆盖同类型的Converter。
对了,忘记提了,这个包有一个Converter接口,如果要自定义的话,也可以自己实现相关的类,注册到这个Bean上,然后统一使用这个Bean。
service provider framework
之所以想到这个,是因为前几天刚刚开始看《Effective Java》这本书中,而里面说的service provider framwork的结构,和这个非常类似。
三个要素:
service接口 => Converter接口
register API => register方法
access API => ConverterUtils工具类
体现的思想就是:客户端和实现类解耦,参照上面的,客户端只要知道ConvertUtils或者ConvertUtilsBean这个类就好了,不需要去记该用哪个Converter。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库