【问题标题】:Type inference in Java (à la C#) [closed]Java 中的类型推断(à la C#)[关闭]
【发布时间】:2011-01-01 13:28:13
【问题描述】:

自从我听说过类型推断(在 Haskell 中),我一直认为 Java 正好相反,即它没有类型推断。不过最近,我有一个瞬间,意识到 Java 在其泛型实现中采用了类型推断。

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

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

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

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

你怎么看?

【问题讨论】:

  • 有趣的是,您使用 var 关键字作为示例 - 当与 C# 匿名类型一起使用时,它提供了一个类型推断的鲜明示例,如果不将其融入编译器。
  • @Kirk - 我看不出var 的类型推断必须如何在编译器中进行烘焙。许多动态语言允许您执行类似 C# 的匿名类型(例如 JavaScript)的操作。可插入类型系统可以访问源代码,因此它可以推断出类似匿名类型的东西......(并检查它的结构,因为它没有名称)
  • @Tomas,Javascript 既不是静态类型的也不是编译语言,所以你的比较是错误的。
  • @Kirk - 当然,我并不是在暗示它是。我很高兴地说,可插拔类型系统应该能够处理匿名类型,例如如果有人为 JavaScript 创建了一个可插入的类型系统。我的问题是,是什么让匿名类型的类型推断特别?

标签: java generics type-inference


【解决方案1】:

嗯,我认为 Java 中的类型推断主要是出于历史的原因:作为一种具有强大遗留约束的语言,Java 的改进是谨慎而渐进的(因为JCP 显示,即使某些类型推断的 improvements 管理到 go through)。使用泛型,长期存在的 GJ 实现在包含到 Java 5 之前已经过全面评估。

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

但这并不意味着 Java 中普遍存在类型推断的强大文化。根据the spec

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

我确实认为更多的 Java 类型推断将是一个福音(Scala 在这个方向上已经是 very interesting improvement)。恕我直言,类型推断使带有类型检查器的反馈循环不那么机械,同时也一样健全,让您编写更少的类型,但让您进行类型检查的次数也一样多。由于类型的一个主要好处是指导程序搜索的心理过程(“letting you write within the space of well-typed programs, rather than in the space of ascii turds”),这种与类型检查器交互的舒适感似乎是无价的:你可以拥有一个类型检查器验证你的思维方式是否正确并训练你这样做,而不是让你在每一行都考虑到它。

现在,应该在哪个阶段进行类型推断是另一个问题。我认为希望将“推理器”与运行时分开可以解决遗留问题:它避免了要求您拥有始终向后兼容的类型推理算法。但是关键就变成了您的标准/主要库的样子:您发布和与其他人交换的源是否带有注释?

虽然可以对带注释的源进行类型检查,但无论推理引擎的强度如何,我仍然希望在编译器中有一个类型推理器,因为这不仅仅是我不想 List<CacheDecoratorFactory> cacheDecoratorFactories = new ArrayList<CacheDecoratorFactory>();,我不想它。就此而言,当我重构预先存在的源代码时,我也不想处理它。在与源交互之前,我需要一个类型“隐藏器”擦除注释,但是如果类型推理引擎 not 完整,which 注释要擦除的问题,并确保类型重构后的擦除是双射变得棘手(尤其是如果您的推理引擎没有返回principal type)......如果我们必须解决一个棘手的问题无论如何 em>,为什么不让它成为一个好的、尽可能完整的类型推理算法呢?我的预感是,超过一定的质量水平(特别是返回类型的一般性),遗留问题将开始消退。

【讨论】:

  • 我很喜欢这个答案。也非常有据可查。 +1
【解决方案2】:

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

myMethod();

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

我选择日期部分和 ++P,它建议添加一个 int 类型作为参数。它会将此日期内联到所有调用者中。

myMethod(20101112);

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

我将“new FileInputStream(”放在开头并引入一个局部变量。++V

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

它强调这可能会引发异常,我可以通过多种方式自动修复。我选择 + 并将异常添加到方法的 throws 子句中。

myMethod(20101112);

public void myMethod(int date) throws FileNotFoundException {
    FileInputStream fileInputStream = new FileInputStream("/tmp/"+date+"/data.file");

恕我直言,让 IDE 完成这项工作更有意义,因为它可以比编译器进行更多的交互操作,并且您可以清楚地看到您的类型变成了什么。

【讨论】:

  • 我希望 IntelliJ 能够隐藏带有“var”的“折叠”文本的类型。单击“var”将展开声明。这将使代码在这方面看起来更像 C# 代码。
【解决方案3】:

这并不是真正的答案,但顺便说一句,您可能想研究 D 语言。它允许您编写如下代码:

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

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

【讨论】:

  • 下一版本的 c++ 标准将支持同样的事情。语法相同。
  • 我知道auto会被支持,但不知道typeof();谢谢分享!只是好奇,是否也可以在 C++0x 中找到一个对象的父对象的类型?
  • 你在找typeid(obj)吗?:cplusplus.com/doc/tutorial/typecasting
  • @Baltasarq:不,我不是;那不一样。 typeid 返回一个 type_info,但你不能在编译时使用它来指定变量的类型。
【解决方案4】:

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

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

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

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

【讨论】:

  • 确实,我知道泛型是在 Java 中使用类型擦除来实现的(我相信这又与 Gilad Bracha 的观点有关,即类型系统应该与语言运行时分开,也见于这段视频:bit.ly/eSVcQm,他说他更喜欢类型擦除)。但我不明白为什么没有理由支持直播var。至少对我来说,读和写看起来不像List<CacheDecoratorFactory> cacheDecoratorFactories = new ArrayList<CacheDecoratorFactory>();的东西更容易
  • 当然。也许我不清楚:我同意。
  • 变量类型推断 ala C# 的 var 声明不是“研究主题”,毫无疑问它与实际编程相关。它使开发人员免于过度冗长的声明,这些声明会使代码混乱,大量的额外输入,并且可以在需要修改的显式类型声明较少时使重构代码更容易。它可以相当容易地实现,并且不需要 JVM 更改。我真的不明白为什么它现在还没有进入语言。
  • 如果您的意思是使用“var”来缩短声明,那么您是对的,@Cristohpher Barber。但是,OP 提到了论文“可插拔类型系统”,这远不止于此。我建议你阅读它,它很有趣。
猜你喜欢
  • 2011-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-20
  • 2018-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多