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