类型擦除不仅仅是可以打开或关闭的字节码功能。
它会影响整个运行时环境的工作方式。如果希望能够查询泛型类的每个实例的泛型类型,则意味着为泛型类的每个对象实例化创建类似于运行时表示形式的元信息。Class
如果你写你不仅要创建三个对象,你可能会创建三个额外的元对象,以反映类型、、和,如果它们以前不存在的话。new ArrayList<String>(); new ArrayList<Number>(); new ArrayList<Object>()
ArrayList<String>
ArrayList<Number>
ArrayList<Object>
考虑到在典型应用程序中使用了数千种不同的签名,其中大多数从未在需要此类反射可用性的地方使用过(由于缺少此功能,我们可以得出结论,目前,所有这些签名都可以在没有此类反射的情况下工作)。List
当然,这成倍增加,一千种不同的泛型列表类型意味着一千种不同的泛型迭代器类型,一千个分离器和流化身,甚至不算实现的内部类。
它甚至会影响没有对象分配的地方,这些地方当前正在引擎盖下进行类型擦除,例如,或,等,每次调用它们时都会返回相同的实例。如果您坚持要以反射方式检查捕获的泛型类型,这将不再起作用。所以如果你写Collections.emptyList()
Function.identity()
Comparator.naturalOrder()
List<String> list=Collections.emptyList();
List<Number> list=Collections.emptyList();
您必须收到两个不同的实例,每个实例报告不同的实例或将来的等效项。getClass()
似乎,希望获得这种能力的人对他们的特定方法有狭隘的看法,如果他们能够反思地找出一个特定参数是否真的是两种或三种类型中的一种,那就太好了,但从不考虑携带元信息的权重,这些信息可能成百上千个泛型类的数百或数千个泛型实例化。
这是我们必须问我们得到回报的地方:支持可疑编码风格的能力(这就是由于通过Reflectlease找到的信息而改变代码行为的全部内容)。
到目前为止,答案只解决了删除类型擦除的简单方面,即对实际实例类型进行内省的愿望。实际实例具有可以报告的具体类型。正如用户the8472的评论中提到的,删除类型擦除的要求通常还意味着希望能够通过 强制转换为数组或通过 创建数组,或通过 访问类型变量的类型。(T)
new T[]
T.class
这将引发真正的噩梦。类型变量与具体实例的实际类型不同。类型变量可以解析为 a,例如 举一个(相当简单)的例子。提供必要的元信息将意味着不仅对象分配变得更加昂贵,每个方法调用都可能带来这些额外的成本,甚至更大,因为我们现在不仅在谈论泛型类与实际类的组合,而且还讨论每个可能的通配符组合,甚至是嵌套的泛型类型。? extends Comparator<? super Number>
请记住,类型参数的实际类型也可以引用其他类型参数,从而将类型检查变成一个非常复杂的过程,您不仅必须对每个类型强制转换重复此过程,如果您允许从中创建数组,则每个存储操作都必须重复它。
除了沉重的性能问题之外,复杂性还引发了另一个问题。如果您查看Stackoverflow的错误跟踪列表或相关问题,您可能会注意到该过程不仅复杂,而且容易出错。目前,的每个次要版本都包含有关泛型类型签名匹配的更改和修复,从而影响将接受或拒绝的内容。我很确定,你不希望内部JVM操作(如类型转换,变量赋值或数组存储)成为这种复杂性的受害者,对每个版本中的合法或不合法有不同的想法,或者由于规则不匹配而突然拒绝编译时接受的内容。javac
javac
javac