【发布时间】:2016-04-05 16:02:23
【问题描述】:
让我先说一下这个问题,我了解 Java 能做什么和不能做什么,我不会问这个问题。我想知道从 JVM 和编译器的角度来看,实际的技术挑战是什么,需要编译器按照它的方式运行。
每当我看到有关 Java 类型擦除的弱点或最讨厌的方面的讨论时,似乎总是在 Java 开发人员列表的顶部附近(这是给我的!)。如果我的历史是正确的,那么 Java 1.0 除了传递对象并重铸它们之外,永远不会实现任何类型检查。当需要更好的 Type 系统时,Sun 必须在完整的 Typing 支持(会破坏向后可比性)或使用他们选择的不会破坏旧代码的泛型解决方案之间做出决定。
与此同时,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();
}
}
这个例子,假设我没有在某处搞砸语法,显示了类型擦除的两个特殊烦恼,乍一看似乎它们应该更容易处理。
首先,我不得不添加一个 FinalType 参数,然后才能引用 BuilderType extends GenericBuilder,尽管看起来 FinalType 似乎可以从 BuilderType 推断出来。我说不太相关,因为这可能更多是关于泛型语法/实现,然后编译器限制强制类型擦除。
第二个问题是我必须将我的 BuilderClass 对象传递给构造函数,以便使用反射来构建构建器,尽管它已经由泛型定义。编译器似乎相对容易存储这里使用的泛型类(只要它不使用 ? 语法)以允许反射查找泛型然后构造它。
由于没有这样做,我认为没有这样做是有充分理由的。我试图了解这些原因是什么,是什么迫使 JVM 坚持类型擦除以保持向后兼容性?
【问题讨论】:
-
Java 1.0 没有类型检查?请。可能你的意思是别的,但就目前而言,这很荒谬。
标签: java generics compilation jvm