【转】为什么 Java 不允许 List<ClassName>.class 这样的操作?

原地址:为什么 Java 不允许 List.class 这样的操作? - 侠吧

我知道有泛型擦除,但是我不知道该如何用泛型擦除来解释这个现象

在被一个序列化工具坑过后,经过测试,发现了一个我以前没注意到的 Java 特性盲点:

Java 允许这样操作:
List.class
但是他不允许这样的操作:
List<ClassName>.class
所以如果你有一个泛型方法需要传入 List<ClassName>的 Class<T> 类型的时候,用如下方式是错的

Jackson2JsonRedisSerializer<List<ClassName>> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<List<ClassName>>(List<ClassName>.class);

只能用如下丑陋的方式

Jackson2JsonRedisSerializer<List> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<List.class>(List.class);

于是,你想限定 List 容器里装什么,对不起,你限制不了,而且这玩意还会给你个警告,根据放狗的结果,不上压制警告注解的注解没法解决

然后我又够了好久,才发现还有一个迂回战术,能让你对 List 容器内部的类型进行限制,写法是下面这样的:

Jackson2JsonRedisSerializer<List<CustomerInfoDto>> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<List<CustomerInfoDto>>(
(Class<List<CustomerInfoDto>>) new ArrayList<CustomerInfoDto>().getClass());

新建一个容器的实现类泛型容器,然后调它的 getClass 方法。。。
虽然它也有一个必须用注解压制的警告,但是好歹能工作啊。

我自己查阅了不是关于 Java class 属性的资料,只能发现这玩意是编译后就被定义的属性,来自虚拟机本身,但是没有解释为什么 List<ClassName>.class 是不允许的。
所以,我的问题就是如何解释这个现象,其次就是如果我要拿到类似 List<ClassName> 这样的类型的 Class;只能像上面那种迂回手段那样建哥对象让后 getClass 吗?

第 1 条附言 · 2018-10-18 17:55:47 +08:00
补充说明一下,貌似泛型擦除问题是没法绕过的,我的这个方式

Jackson2JsonRedisSerializer<List<ClassName>> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<List<ClassName>>((Class<List<ClassName>>) new ArrayList<ClassName>().getClass());

和楼下一位朋友的方式

 Jackson2JsonRedisSerializer<List<ClassName>> jackson2JsonRedisSerializer = (Jackson2JsonRedisSerializer<List<ClassName>>) new Jackson2JsonRedisSerializer<>((Class<?>) List.class);

仅仅是能编译通过,但是,最后转回并得到的仍然不是我想要的 List<ClassName> 泛型类型的对象,很奇葩的是,转换过程并不报错,但是,当你从 List<ClassName> 中取出一个对象,运行 clasName.getClass()的时候,就报错:
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to com.github.domain.ClassName
确实像某些人说的那样,如果要借助 Jackson 的序列化器反序列化容器对象,必须借助 Typereference,但是 Jackson2JsonRedisSerializer 本身不接受 Typereference 传入,所以目前没办法


可参考评论

#3
blindpirate Thu, 31 Oct 2018 16:55:42
因为不存在 List<ClassName>.class,只有 List.class。至于为什么,需要从历史上讲起: https://www.zhihu.com/question/28665443

#7
BBCCBB Thu, 31 Oct 2018 17:04:37
TypeFactory.defaultInstance().constructCollectionType(ArrayList.class, MyBean.class);

可能是因为你对 jackson 不熟.

#9
xingda920813 Thu, 31 Oct 2018 17:22:34
@abcbuzhiming 抛开本例中的 Jackson 不谈 (因为有其他的 API, 如上 @BBCCBB 回答的), 只论纯 Java 的话.

可以这样用: Jackson2JsonRedisSerializer<List<SomeClass>> serializer = (Jackson2JsonRedisSerializer<List<SomeClass>>) new Jackson2JsonRedisSerializer<>((Class<?>) List.class);

这样生成的 serializer 仍然是泛型化的.

#10
gaius Thu, 31 Oct 2018 17:41:05
new TypeRefernece<List>(){}

#12
abcbuzhiming Thu, 31 Oct 2018 17:57:49
@xingda920813 朋友,我楼顶补充说明一下,你这个方法也是没效的,仅仅不报错了,而且很奇葩的是,转换得到的其实是 List类型,这个类型赋值给 List的时候,系统是不报错的,但是,如果你从最后的 List中取出一个对象,执行这个对象的方法的时候,就报错了,告诉你类型转换不能

#14
lovedebug Thu, 31 Oct 2018 18:17:23
因为类型擦除发生在编译阶段,导致所有 list都变成了 list,对应 class 都是 list.class。

#15
TommyLemon Thu, 31 Oct 2018 18:27:09
Gson 也差不多:

List<Person> people = gson.fromJson(jsonData, new TypeToken<List<Person>>(){}.getType());

FastJSON 很方便:

List<Person> people = JSON.parseArray(jsonData, Person.class);

不支持 Class.class 就是因为 Java 为了兼容 1.4 及以下的 JVM,
实现时用的是 泛型擦除 方式,只能在运行前静态检查类型,
编译通过后 Type 就换成 Object 了,只能强转获取。
这个你看下 ArrayList 的源码就知道了,
里面是用 Object[] elementData 来存列表数据的,
get(int position) 内 return 的时候强转:

public E get(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

return (E) elementData[index];
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值