Java中的类型推断(àlaC#)

时间:2011-01-01 13:28:14

标签: java generics type-inference

自从我听说类型推断(在Haskell中)以来,我就认为Java恰恰相反,即它没有类型推断。最近,我有一个aha时刻,并意识到Java在其泛型实现中使用类型推断。

然后,我读了Gilad Bracha的两篇论文(根据我的理解,这是Java中泛型实现背后的人之一)。第一篇论文是tutorial about generics(PDF),其中他明确表示编译器推断用于替换格式类型参数的实际类型参数。所以,Java中有类型推断,但为什么只有泛型,为什么不能像C#的var关键字?这是我的问题。

为什么Java没有内置到编译器中的更多类型推断?

我会建议一个答案,这与我读到的第二篇论文Pluggable Type Systems(PDF)有关。看来,Gilad Bracha认为推理部分不应该是编译器的一部分,而是IDE功能或类似功能(上述论文中的第4节第6段):

  

更好的工程方法是   将类型推断实现为单独的   工具,可在IDE中使用。   找到输入类型的程序员   注释很烦人可以调用   按需推理。

您怎么看?

4 个答案:

答案 0 :(得分:2)

类型推断在IntelliJ中可用,也可能在其他IDE中可用。您可以编写表达式(或使用现有表达式)并选择“引入字段/局部变量/常量”等,它将为您提供一些推断的类型选项和一些建议的名称。如果表达式出现多次,则可以选择替换所有出现的内容。例如说我有一个字符串我想变成一个参数

myMethod();

public void myMethod() {
    "/tmp/20101112/data.file"
}

我选择了日期部分和< ctrl> +< alt> + P,它建议添加一个int类型作为参数。它会将此日期内联到所有呼叫者。

myMethod(20101112);

public void myMethod(int date) {
    "/tmp/"+date+"/data.file"
}

我在开始时放置“new FileInputStream(”并引入一个局部变量。< ctrl> +< alt> + V

    FileInputStream fileInputStream = new FileInputStream("/tmp/"+date+"/data.file");

它强调这可以抛出异常,我可以自动修复多种方式。我选择< alt> +< enter>并将该异常添加到方法的throws子句中。

myMethod(20101112);

public void myMethod(int date) throws FileNotFoundException {
    FileInputStream fileInputStream = new FileInputStream("/tmp/"+date+"/data.file");
恕我直言,让IDE完成工作更有意义,因为它可以比编译器更具交互性,你可以明确地看到你的类型变成了什么。

答案 1 :(得分:2)

这不是一个真正的答案,但从侧面说明,你可能想要研究D语言。它允许您编写如下代码:

int*[6]*[wstring][]*[string]*[] myVar;
auto myVar2 = new typeof(myVar[0])[100]; // equivalent to: new int*[6]*[wstring][]*[string]*[]*[string]*[100]

基本上,它是手动推理+自动推理,它可以让你编写非常通用的代码,用其他语言编写起来比较困难。 (这里的例子不太现实,但它说明了这一点。)

答案 2 :(得分:2)

嗯,我认为类型推断 在Java中主要是因为历史的原因:因为对于具有强大遗留约束的语言而言,Java的改进是谨慎的。逐步增加(如JCP所示,即使某些improvements类型推断设法为go through)。使用泛型,在包含在Java 5中之前,对长期GJ实现进行了彻底的评估。

  

在Java 5发布之前,Java中没有类型推断。 (...)当在Java 5中引入泛型(...)时,该语言保留了对变量,方法和分配的这一要求。但是多态方法的引入(按类型参数化)决定了(i)程序员在每个多态方法调用站点提供方法类型参数或(ii)语言支持方法类型参数的推断。为避免给程序员带来额外的文书负担,Java 5的设计者选择执行类型推断来确定多态方法调用的类型参数。 (source

但这并不意味着Java中存在普遍类型推理的强大文化。根据{{​​3}}:

  

另请注意,类型推断不会以任何方式影响健全性。如果推断的类型是无意义的,则调用将产生类型错误。类型推断算法应被视为启发式算法,旨在在实践中表现良好。如果它无法推断出所需的结果,则可以使用显式类型参数。

我认为Java的更多类型推断将是一个福音(Scala在该方向已经是the spec)。恕我直言,类型推断使得类型检查器的反馈循环不那么机械,同时就像声音一样,让你可以编写更少的类型,但同样可以进行类型检查。由于类型的一个主要好处是指导程序搜索的心理过程(“very interesting improvement”),这种与类型检查器交互的舒适性似乎是非常宝贵的:你得到一个类型 - 检查器验证您是否用良好的术语进行思考并训练您这样做,而不是让您在每一行都考虑到它。

现在,应该发生类型推断的阶段是另一个问题。我认为希望将“推理器”与运行时区分开来解决传统问题:它避免要求您使用始终向后兼容的类型推断算法。但关键是你的标准/主要库看起来像:你发布的源和&是否与其他人交换注释?

虽然无论你的推理引擎的强度是什么,值得注意的是,带注释的源都可以进行类型检查,但我仍然需要编译器中的类型推理器,因为它不仅仅是我不想要的 List<CacheDecoratorFactory> cacheDecoratorFactories = new ArrayList<CacheDecoratorFactory>();,这是我没有想要读取它的事件。在重构预先存在的源时,我也不想处理它。在与源交互之前,我需要一个类型“hider”擦除注释,但如果类型推理引擎完成, 注释要擦除的问题,并确保类型重建之后的擦除是双射变得棘手(特别是如果你的推理引擎没有返回letting you write within the space of well-typed programs, rather than in the space of ascii turds)......如果我们必须解决一个棘手的问题 ,为什么不把它作为一个好的,尽可能完整的类型推理算法呢?我的预感是,经过一定程度的质量(特别是返回类型的普遍性),遗留问题将开始消退。

答案 3 :(得分:1)

这是一个有趣的,非常有趣的话题,与研究有关,而不是与实际(即当前)编程有关。

首先要做的事情。关于Java中的var,实际上没有理由实现它,它们已经拥有了“技术”。但是,泛型仅在系统的编译器端,这意味着在其运行时,VM只是使用对Object的引用,并且由于编译器注入的代码(再次在编译时),它们被适当地转换。但是,在C#中,泛型在编译之后会存在。

其次,关于Bracha的那篇(非常有趣的)论文,你应该看看我们的StaDyn项目,一种类似C#的编程语言。类型系统实际上是可插入的,即您可以像普通C#一样使用它,或者根本不使用它,并体验完全动态的语言。

http://www.reflection.uniovi.es/stadyn/