转:有关Java泛型的类型擦除(type erasing)

转载自:拈花微笑

自从Java 5引入泛型之后,Java与C++对于泛型不同的实现的优劣便一直是饭后的谈资。在我之前的很多training中,当讲到Java泛型时总是会和C++的实现比较,一般得出的结论是

  • Java使用类型擦除(type erasing),泛型信息只在编译时供javac作类型检查用,在编译后便被javac擦除,因此无法被反射
  • C++使用代码模板实现泛型,即在预处理时会生成类似?list_int?,?list_char?等的泛型类,虽然解决Java的运行时伪泛型的问题,但是会导致编译后的代码呈线性增长
  • 于是在一般情况下,Java的类型擦除实现较优

这三条已经比绝大多数的Java培训讲师讲得深刻了。但是如果下面有人问?在什么情况下C++的实现方式较优?时,除了无法被反射,我很难现抓一个有说服力的例子。今天,Spring的RestTemplate终于给了我这个例子

我遇到的问题如下(阅读需要有一些Spring MVC、REST基础)

有一个返回类型为List的MVC方法:

 Java |     copy code | ?  
1  
2
@RequestMapping(value = "...", method = RequestMethod.GET)
3
@ResponseBody
4
public List<DomainClass> doSomethingREST() {
5
    List<DomainClass> domainObjs = ... ;
6
    return domainObjs;
7
}
8  

在运行时,Spring MVC会将List转换成JSON字符串并返回至客户端。但是如果我在另一个service中使用RestTemplate直接调用该REST接口,问题便来了:

 Java |     copy code | ?  
1  
2
List<DomainClass> domainObjs = this.restTemplate.getForObject(this.restURL, List.class);
3  

关键在于第二个参数,是返回值的类型,RestTemplate会根据此类型选择适当的MessageConverter将调用REST接口的返回值反序列化为与类型匹配的对象。由于List的泛型参数在编译时被擦除,于是RestTemplate便无法确定List中的内容。?一般情况下?(REST接口返回值不是List类型)不会有问题,但是巧合的是,如果服务器端使用JSON对REST结果进行序列化,返回值会被封装在List中,此时,Spring已无法判断开发人员是想直接获得List封装的JSON内容还是需要更进一步使用Jackson库将JSON反序列化为Java对象。现实情况下,Spring选择的前者,于是便会抛出ClassCastException

上网搜了一圈,已经有人向Spring提交了enhancement请求(这里这里,其中第一个链接中的walkaround可以work),但是目前没有milestone

– EOF –

转:有关Java泛型的类型擦除(type erasing)

时间: 2024-10-11 14:18:38

转:有关Java泛型的类型擦除(type erasing)的相关文章

Java泛型:类型擦除

类型擦除 代码片段一 Class c1 = new ArrayList<Integer>().getClass(); Class c2 = new ArrayList<String>().getClass(); System.out.println(c1 == c2); /* Output true */ 显然在平时使用中,ArrayList<Integer>()和new ArrayList<String>()是完全不同的类型,但是在这里,程序却的的确确会输

Java泛型之类型擦除

类型擦除 学过C++模板的,在使用Java泛型的时候,会感觉到有点不疑问,例如:(1)无法定义一个泛型数组.无法调用泛型参数对象中对应的方法(当然,通过extends关键字是可以做到,只是比较麻烦):(2)ArrayList<Integer>和ArrayList<String>在运行时的类型是相同的.Java中的泛型有这些问题,是它的实现机制决定的,即"类型擦除". 类型擦除的定义:编译通过后,准备进入JVM运行时,就不再有类型参数的概念,换句话说:每定义一个泛

获取java泛型参数类型

1.Type和Class的区别 简单来说,Class实现了Type接口. Type源码定义: package java.lang.reflect; /**  * Type is the common superinterface for all types in the Java  * programming language. These include raw types, parameterized types,  * array types, type variables and pri

Java泛型之类型未被擦除

大家都知道Java源码在编译之后会擦除泛型信息,现在来看一个泛型未被擦除的情况,见ConcurrentHashMap.comparableClassFor方法. ParameterizedType.getActualTypeArguments能获取类所实现的接口中未被擦除的泛型信息.实验如下: public class Parameterized { public static void main(String[] args) { Type[] types = Integer.class.get

Java-泛型编程-类型擦除(Type Erasure)

Java中的泛型代码和C++中的模板有一个很大的不同:C++中模板的实例化会为每一种类型都产生一套不同的代码,这就是所谓的代码膨胀. Java中并不会产生这个问题.虚拟机中并没有泛型类型对象,所有的对象都是普通类. 虚拟机中的泛型转换需要记住4条事实: 1) 定义任何一个泛型都会自动产生其原始类型(raw type) 2) 这个过程中,泛型类型会被擦除,替换为其限定类型(bounding type) 3) 必要时插入强制转换来保证类型安全 4) 使用桥接方法(bridge method)来保证正

泛型 Generic 类型擦除引起的问题及解决方法

参考:http://blog.csdn.net/lonelyroamer/article/details/7868820#comments 因为种种原因,Java不能实现真正的泛型,只能使用类型擦除来实现伪泛型,这样虽然不会有类型膨胀的问题,但是也引起了许多新的问题.所以,Sun对这些问题作出了许多限制,避免我们犯各种错误. 1.先检查,再编译,以及检查编译的对象和引用传递的问题 既然说类型变量会在编译的时候擦除掉,那为什么我们往ArrayList<String> arrayList=new

[运行时获取模板类类型] Java 反射机制 + 类型擦除机制

给定一个带模板参数的类 class A<T> { } 如何在运行时获取 T的类型? 在C#中,这个很简单,CLR的反射机制是解释器支持的,大概代码为: namespace TestReflect { class Program<T> { public Type getTClass() { Type type= this.GetType(); Type[] tts = type.GetGenericArguments(); return tts[0]; } } } 可是在Java中,

ParameterizedType获取java泛型参数类型

https://blog.csdn.net/qq_18242391/article/details/54251947 前言 这两天在看以前写的ssh项目时,遇到一个问题就是封装的BaseDaoImpl抽象类,构造方法里面是这样写的. Class<T> clazz; public BaseDaoImpl() { ParameterizedType pt = (ParameterizedType)getClass().getGenericSuperclass(); clazz = (Class&l

Java中的类型擦除与桥方法

类型擦除 Java在语法中虽然存在泛型的概念,但是在虚拟机中却没有泛型的概念,虚拟机中所有的类型都是普通类.无论何时定义一个泛型类型,编译后类型会被都被自动转换成一个相应的原始类型. 比如这个类 public class Parent<T> { public void sayHello(T value) { System.out.println("This is Parent Class, value is " + value); } } 在编译后就变成了 public c