欢迎光临散文网 会员登陆 & 注册

关于Java的TypeReference

2022-01-08 21:39 作者:友纪V-入OP  | 我要投稿

我们知道,Java中的泛型是编译期的,在运行时其会被擦除掉,比如我们编写代码List<Integer> lst = new ArrayList<>();,从运行时看来将会是List lst = new ArrayList();,只留下了原始类型(raw type)。

但我们有时候确实需要在运行时获取一定的泛型信息。考虑这样的情况:在一个servlet应用里(为什么是servlet?因为spring mvc遇不到这个问题),我们要求前端使用JSON来发送请求,并规定了请求的格式——

为此,对应的POJO为——

在servlet中,我们需要将请求体字符串转换为特定的RequestDto<T>。比如某个接口要求前端发送RequestDto<List<Integer>>。我们在servlet中可能得这么写——

但这个通不过编译——所谓的RequestDto<List<Integer>>.class是不存在的,因为在运行时不存在泛型类型,我们只能得到RequestDto.class,所以只能这么写——

虽然有个恼火的警告,但至少能编译了。我们整个demo试试——

成了!我们再试试错误的输入?

抛异常了!这符合预期,但是却是在req.getData()时抛的cast异常,而非json转换时抛出异常。

这是肿么回事呢?从运行时看来,我们是在试图将字符串{"type":"search", "data": "hello world!"}转换成类型RequestDto,即——

这河里吗?可太合理了,既然是Object,那是任何类型都是可以的了。但这显然是不符合我们的需要的——如果类型的错误必须要在我们使用的时候才能暴露出来,那这和动态类型语言何异?

问题就出在Java的泛型擦除机制。我们有什么手段来规避它吗?库函数的设计者告诉我们,有!

Java的泛型擦除机制实际上至少在两个地方没有擦掉——方法的参数和返回值;继承泛型类的类。

获取其的demo如下——

前者显然为Spring mvc所利用——控制器的接口能够正确处理泛型类,而后者则是所谓的TypeReference所利用的——通过继承的方式来保存泛型信息。我们可以通过匿名实现类来在行内(inline)直接拿到该信息。

这样,我们实际上就能够间接地表示RequestDto<Integer>.class了。对上面的json反序列化的代码,我们可以使用TypeReference的匿名实现类而非class来保留泛型信息——

我们仍旧会得到一个异常,但这个异常是符合预期的,容易理解的,是在进行反序列化中抛出的!


关于Java的TypeReference的评论 (共 条)

分享到微博请遵守国家法律