为什么Java必须采用类型擦除来保持向后兼容性?

为什么Java必须采用类型擦除来保持向后兼容性?,java,generics,compilation,jvm,Java,Generics,Compilation,Jvm,让我在开始这个问题之前说,我理解Java能做什么和不能做什么,我不想问这个问题。我想知道,从JVM和编译器的角度来看,真正的技术挑战是什么,需要编译器按照它的方式运行 每当我看到关于java类型擦除的弱点或最讨厌的方面的讨论时,java开发人员似乎总是把它放在最前面的位置(对我来说是这样!)。如果我的历史记录是正确的,那么Java 1.0除了传递对象和重铸对象之外,永远不会实现任何类型检查。当需要一个更好的类型系统时,Sun必须在完全的类型支持(这将破坏向后的可比性)和选择不破坏旧代码的泛型解决

让我在开始这个问题之前说,我理解Java能做什么和不能做什么,我不想问这个问题。我想知道,从JVM和编译器的角度来看,真正的技术挑战是什么,需要编译器按照它的方式运行

每当我看到关于java类型擦除的弱点或最讨厌的方面的讨论时,java开发人员似乎总是把它放在最前面的位置(对我来说是这样!)。如果我的历史记录是正确的,那么Java 1.0除了传递对象和重铸对象之外,永远不会实现任何类型检查。当需要一个更好的类型系统时,Sun必须在完全的类型支持(这将破坏向后的可比性)和选择不破坏旧代码的泛型解决方案之间做出决定

与此同时,C#遇到了同样的问题,走了相反的道路,打破了向后的可比性,大约在同一时间实现了一个更复杂的打字系统(我相信)

我的主要问题是,为什么这两种语言都是非此即彼的问题?编译器进程的哪些方面意味着在不破坏旧代码向后可比性的情况下,无法支持C风格的类型处理?我理解问题的一部分是,确切的类型在编译时并不总是已知的,但乍一看(天真的)它似乎有时可以在编译时已知,或者在编译时未知,并在运行时用某种反射方法处理

问题是实现起来不可行,还是被认为实现运行时解决方案太慢

更进一步,让我们使用一个简单的通用工厂代码示例,作为类型擦除感觉相当麻烦的地方的示例

public class GenericFactory<FinalType, BuilderType<FinalType> extends GenericBuilder<FinalType>>{

   private Class builderClass;

   public GenericFactory(Class<BuilderType> builderClass){
        this.builderClass=builderClass;
   }

   public FinalType create(){

       GenericBuilder builder=builderClass.newInstance();

       builder.setFoo(getSystemProperty("foo");
       builder.setBar(getSystemProperty("bar");
       builder.setBaz(getSystemProperty("baz");

       return builder.build();
   }
}
公共类GenericFactory{
私有类builder类;
公共通用工厂(类生成器类){
this.builderClass=builderClass;
}
公共FinalType创建(){
GenericBuilder=builderClass.newInstance();
builder.setFoo(getSystemProperty(“foo”);
建筑商。立贝塔(getSystemProperty(“bar”);
建筑商:setBaz(getSystemProperty(“baz”);
返回builder.build();
}
}
这个例子,假设我没有把语法搞砸,显示了类型擦除的两个特别麻烦,乍一看似乎它们应该更容易处理

首先,也不太相关,在引用BuilderType Extendes GenericBuilder之前,我必须添加一个FinalType参数,尽管FinalType似乎可以从BuilderType推断出来。我说不太相关,因为这可能更多地是关于泛型语法/实现,而编译器限制了强制类型擦除

第二个问题是,我必须将我的BuilderClass对象传递给构造函数,以便使用反射来构建生成器,尽管它已经由泛型定义。似乎编译器存储这里使用的泛型类比较容易(只要它不使用?语法)允许反射查找泛型,然后构造泛型


由于没有做到这一点,我认为有一个很好的理由不这样做。我试图了解这些原因是什么,是什么迫使JVM坚持使用类型擦除来保持向后兼容性?

我不确定您所描述的(两个“烦恼”)是类型擦除的结果

在引用BuilderType extends GenericBuilder之前,我必须添加一个FinalType参数,尽管FinalType似乎可以从BuilderType推断出来

BuilderType
将不是有效的泛型类型名称,除非我错过了对Java 8中的某些更改。因此,它应该是
BuilderType扩展了GenericBuilder
,这很好。
FinalType
不能在这里推断,编译器如何知道提供哪种类型

第二个问题是,我必须将BuilderClass对象传递给构造函数,以便使用反射来构建生成器,尽管它已经由泛型定义

那不是真的。泛型参数没有定义
FinalType
实际上是什么。我可以创建
GenericFactory
(使用
StringBuilderType扩展GenericBuilder
)以及
GenericFactory
(使用
IntegerBuilderType扩展GenericBuilder

在这里,如果您向变量定义或方法调用提供类型参数,则会发生类型擦除


但是,如果您有一个字段或子类,例如,
私有GenericFactory stringFactory
,则不存在类型擦除。可以从反射数据中提取泛型类型(遗憾的是,没有简单的内置方法,但请看这里:).

Java 1.0没有进行类型检查?拜托。可能你的意思是别的,但就目前的情况来看,这很荒谬。特别是,可能是重复的。