Java为什么不进行类型推断?


44

我一直想知道,既然Java是正确的语言,并且它的VM非常成熟,为什么Java不进行类型推断。Google的Go语言是一种具有出色的类型推断功能的语言示例,它减少了人们必须进行的键入操作。该功能不是Java的一部分有什么特殊的原因吗?


3
向后兼容规则,使用的语言与Java一样古老和流行
棘手怪胎

9
@ratchetfreak不能以向后兼容的方式添加类型推断吗?较旧的程序只会提供比所需更多的类型信息。

1
与Type Erasure一起使用时,可能会产生一些意想不到的效果。docs.oracle.com/javase/tutorial/java/generics/…–
Zachary Yates

4
请注意,Java 8 的Lambda功能将带来很多类型推断:您可以编写复杂的lambda,而无需提及任何类型以及所有推断的内容。
约阿希姆·绍尔

4
Java将进行局部变量类型推断:JEP 286:局部变量类型推断
Jimmy Page

Answers:


60

从技术上讲,Java 在使用泛型时确实具有类型推断。用像这样的通用方法

public <T> T foo(T t) {
  return t;
}

编写时,编译器将分析并了解

// String
foo("bar");
// Integer
foo(new Integer(42));

根据作为参数输入的内容,将为第一个调用返回一个String,为第二个调用返回一个Integer。结果,您将获得正确的编译时检查。此外,在Java 7中,可以得到一些额外的类型推断当实例化泛型像现在这样

Map<String, String> foo = new HashMap<>();

Java足以为我们填补空白。现在,为什么Java不支持将类型推断作为变量分配的一部分?有一次,在变量声明中有一个用于类型推断的RFE,但是由于“将无法解决”而被关闭,因为

人类可以通过两种方式从类型声明的冗余中受益。首先,冗余类型可作为有价值的文档-读者不必搜索getMap()的声明来查找返回的类型。其次,冗余允许程序员声明预期的类型,从而受益于编译器执行的交叉检查。

完成此操作的贡献者还指出,它只是感觉像“非Java风格”,我是一个同意的人。Java的冗长性既可以是祝福,也可以是诅咒,但这确实使语言成为了现实。

当然,那个特定的RFE并没有结束对话。在Java 7期间,再次考虑了此功能,并创建了一些测试实现,其中包括James Gosling本人。再次,此功能最终被击落。

随着Java 8的发布,我们现在将类型推断作为lambda的一部分,例如:

List<String> names = Arrays.asList("Tom", "Dick", "Harry");
Collections.sort(names, (first, second) -> first.compareTo(second));

Java编译器能够查看方法Collections#sort(List<T>, Comparator<? super T>),然后查看的接口Comparator#compare(T o1, T o2)并确定,first并且second应该是,String因此允许程序员放弃必须在lambda表达式中重新声明类型。


5
First, the redundant type serves as valuable documentation - readers do not have to search for the declaration of getMap() to find out what type it returns-是的,如果是的HashMap<String, Integer> map = foo.getMap()话,我同意-在C#var中,即使可以,我通常也不会使用这种情况。但是,如果有的话,这个论点就不会成立HashMap<String, Integer> map = new HashMap<String, Integer>()。这是真正的冗余,我看不到必须两次写入类型名称的好处。以及我如何从编译器交叉检查中受益,我一点也不明白。
康拉德莫拉斯基

9
是的,这对泛型很有用,但是我仍然想念varJava 中C#的等效功能。
Konrad Morawski 2014年

15
至于它是“非Java风格的”,这个(俗气的)故事浮现
Konrad Morawski

6
此外,函数的返回类型通常是显而易见的或不必要的,即使在不是这种情况的情况下,您的IDE也会在半秒钟内标记出该类型。
Phoshi 2014年

8
我相信官方报价Humans benefit from the redundancy是当前问题的真正答案,因为我读过的所有论据似乎都是Java设计师提供的无缘无故的借口:C#和C ++都有此功能,C#和C ++设计师的能力不亚于Java,每个人都很高兴使用它。是什么会导致Java开发人员与C#或C ++开发人员不同?这就是为什么我同意@KonradMorawski的原因,“这是在这里完成事情的方式”似乎再次是真正的幕后原因。
paercebal,2015年

16

好吧,首先,类型推断与运行时的成熟度无关,无论该运行时是使用了30年的CPU还是这么新的VM,这些位仍然闪闪发光。都是关于编译器的。

就是说,泛型被允许使用,非泛型类型不允许使用泛型的原因似乎是由于哲学的原因,没有什么可以阻止设计人员添加它。

更新:看起来Java 10支持它--- http://openjdk.java.net/jeps/286


5

据我所知,在90年代初设计Java时,类型推断在主流语言中并不流行(但是它已经是一个非常著名的概念,例如ML)。因此,我可以想象可能不支持类型推断,因为Java的目标读者是来自C ++,Pascal或其他主流语言的程序员,而这些程序员都没有。

同样,Java的设计原则之一是明确地编写东西,以确保程序员和编译器对代码有相同的理解:重复信息可减少出错的机会。当然,输入更多的字符是否值得提供它提供的额外安全性可能是一个问题,但这就是Java遵循的设计哲学:明确地编写东西。

我不知道Java是否会在将来获得类型推断,但是IMO会是该语言的一大革命(正如Glenn Nelson所提到的那样,它被描述为“非Java类”),然后人们可能还会考虑删除该语言。以新名称命名Java。

如果要通过类型推断使用JVM语言,则可以使用Scala。


2

我可以想到一些可能的原因。一种是显式键入是自我记录。Java通常将此优先于简洁。另一个原因可能是在类型有些含糊的情况下。就像类型或任何子类型可能满足例程一样。假设您要使用一个List,但是有人来使用ArrayList专有的方法。即使您需要编译错误,JIT也会推断一个ArrayList并继续进行。


8
GridBagLayout Gridbag = new GridBagLayout(); 添加到自我文档?其纯粹的重复。
安德斯·林登2013年

3
这与两种类型不相同的情况没有歧义。您可以轻松地分配一个子类的实例。
jiggy 2013年

Let's say you want to use a List, but someone comes along and uses a method exclusive to ArrayList. The JIT would infer an ArrayList and carry on even if you wanted a compilation error-我不明白这一点。类型推断发生在实例化变量的时刻,而不是在其上调用方法。您能否举例说明您的意思?
Konrad Morawski 2014年

2
@KonradMorawski:我假设他的意思是,如果某个方法返回ArrayList,则将类型推断为该类型。如果要将类型视为返回类型以外的其他类型,则不能使用推断。我不明白这在健全的地方的代码库中怎么可能成为问题。
Phoshi 2014年

JIT不会在类型推断中发挥任何作用。类型推断是一种编译时现象。并且如果将变量声明为对列表的引用,则尝试访问其上的ArrayList成员将不会进行类型检查,并且会出现编译错误
sara

0

它违背了公认的建议,即使用最适合您需要的通用接口声明变量,然后使用适当的实现类对其进行初始化,例如

Collection<String> names = new ArrayList<>();

有效,

var names = new ArrayList<String>();

只是语法糖

ArrayList<String> names = new ArrayList<String>();

如果需要的话,您的IDE可以new ArrayList<String>()通过“单击”(重构/创建局部变量)从表达式中生成它,但是请记住,它与“使用接口”建议相抵触。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.