为什么不能为Java中的var关键字分配lambda表达式?

hi.*_*ish 62 java lambda java-10

允许var在Java 10中分配如下字符串:

var foo = "boo";
Run Code Online (Sandbox Code Playgroud)

虽然不允许为其分配lambda表达式,例如:

var predicateVar = apple -> apple.getColor().equals("red");
Run Code Online (Sandbox Code Playgroud)

为什么不能推断的λ或方法引用类型时,它可以推断出休息等String,ArrayList,用户类等?

Bri*_*etz 58

这与此无关var.它与lambda是否具有独立类型有关.方法var是它在RHS上计算初始化器的独立类型,并推断出.

自从它们在Java 8中引入以来,lambda表达式和方法引用没有独立类型 - 它们需要一个目标类型,它必须是一个功能接口.

如果你试试:

Object o = (String s) -> s.length();
Run Code Online (Sandbox Code Playgroud)

你也得到一个类型错误,因为编译器不知道你打算将lambda转换为什么功能接口.

要求推理var只会让它变得更难,但由于更容易回答的问题无法解决,更难的问题也无法解决.

请注意,您可以通过其他方式(例如强制转换)提供目标类型,然后它可以工作:

var x = (Predicate<String>) s -> s.isEmpty();
Run Code Online (Sandbox Code Playgroud)

因为现在RHS有一个独立的类型.但是最好通过提供x清单类型来提供目标类型.


Jor*_*nee 54

局部变量类型推断JEP:

推理过程基本上只给变量赋予其初始化表达式的类型.一些微妙之处:

  • 初始化程序没有目标类型(因为我们尚未推断它).需要这种类型的多表达式(如lambdas,方法引用和数组初始值设定项)将触发错误.

因为lambda表达式本身没有类型,所以无法推断它var.


...同样,可以设置默认规则.

当然,你可以想出一种方法来解决这个限制.为什么开发人员做出不这样做的决定真的取决于猜测,除非参与决策的人能在这里回答.如果您有兴趣,可以在openjdk邮件列表中查询:http://mail.openjdk.java.net/mailman/listinfo

如果我猜测,他们可能不希望在var一组特定的功能接口类型的上下文中绑定lambda推理,这将排除任何第三方功能接口类型.更好的解决方案是推断(Apple) -> boolean可以转换为兼容的功能接口类型的通用函数类型(即).但是JVM没有这样的函数类型,并且在创建lambda表达式的项目中已经做出了不实现它们的决定.如果你对具体原因感兴趣,请问开发者.

  • 搜索兼容的功能接口不是性能问题; 这是一个心灵阅读的问题.单独的JDK中有多个功能接口与示例兼容; 您希望编译器如何选择您想到的那个? (10认同)
  • 考虑到javac`*在一般情况下进行的优化很少*在编译时进行潜在的搜索实现poly表达式我猜是疯了 (3认同)
  • @JornVernee你错过了我的观点,有两种方式.1.`(String s) - > s.isEmpty()`已经兼容`Function <String,Boolean>`和`Predicate <String>`等.要求选择是要求思维阅读.如果我们添加了新的,那么可能会导致人们程序中的类型发生变化,或者现有程序不再编译.馊主意.2."java.util.function"中的功能接口不是魔术或语言的一部分; 它们只是普通的接口.用户只需说出他们所说的类型就好了. (2认同)
  • @BrianGoetz 我错过了重点吗?我们似乎提倡同样的事情。我在答案中试图指出的一点是,您总是可以想出某种方法来使其发挥作用,但这并不意味着该解决方案是一个_好的_解决方案。但是像“为什么他们不直接做 X”(我知道你喜欢得到这些)这样的问题一般来说 SO 社区并不能真正明确地回答,尽管我们可以推测。 (2认同)

Kar*_*ldt 27

对于那些说这是不可能的,不受欢迎的或不想要的人,我只想指出Scala可以通过仅指定参数类型来推断lambda的类型:

val predicateVar = (apple: Apple) => apple.getColor().equals("red")
Run Code Online (Sandbox Code Playgroud)

在Haskell中,因为它getColor是一个独立的函数而不是附加到一个对象,并且因为它完全是Hindley-Milner推断,所以你甚至不需要指定参数类型:

predicateVar = \apple -> getColor apple == "red"
Run Code Online (Sandbox Code Playgroud)

这非常方便,因为程序员明确指定的不是简单的类型,而是更复杂的类型.

换句话说,它不是Java 10中的一个功能.这是它们的实现和以前的设计选择的限制.

  • 由于提及HM推理算法并提供上下文而被上调. (8认同)

Ste*_*Toe 5

要回答这个问题,我们必须详细了解 lambda 是什么以及它是如何工作的。

首先我们应该了解什么是 lambda:

一个 lambda 表达式总是实现一个函数式接口,所以当你必须提供一个函数式接口时Runnable,而不是必须创建一个实现该接口的全新类,你可以只使用 lambda 语法来创建一个函数式接口的方法需要。请记住,尽管 lambda 仍然具有它正在实现的功能接口的类型。

考虑到这一点,让我们更进一步:

这在 Runnable 的情况下效果很好,我可以像这样创建一个新线程,new Thread(()->{//put code to run here});而不是创建一个全新的对象来实现功能接口。这是有效的,因为编译器知道它Thread()采用 Runnable 类型的对象,因此它知道 lambda 表达式必须是什么类型。

但是,在将 lambda 分配给局部变量的情况下,编译器不知道此 lambda 正在实现什么功能接口,因此无法推断var应该是什么类型。由于它可能正在实现用户创建的功能接口,或者它可能是runnable接口,因此无法知道。

这就是 lambda 不能与 var 关键字一起使用的原因。

  • 我不认为 lambda 表达式“只是实现功能接口的语法糖”。无论如何,这与这个本来不错的答案无关。考虑重新措辞该段落。 (4认同)

Ous*_* D. 5

正如几个人已经提到的,应该var推断什么类型,为什么要推断?

该声明:

var predicateVar = apple -> apple.getColor().equals("red");
Run Code Online (Sandbox Code Playgroud)

是不明确的,没有有效的理由编译器应该挑选Function<Apple, Boolean>Predicate<Apple>或反之亦然假设apple在lambda标识符表示Appleisntance。

另一个原因是lambda本身没有可说的类型,因此编译器无法推断出它。

同样,“如果可能的话”可以想象一下开销,因为每次将lambda分配给var变量时,编译器都必须遍历所有功能接口并确定哪个功能接口最合适。

  • 兼容的功能接口不容易互换的事实已经是一个更大的问题,而 var 也正面临着这个问题 (2认同)