1

所以我的函数式程序员喜欢像 python 这样将函数视为一等公民的语言。看起来 Java 8 屈服于压力并且“某种”实现了诸如 lambda 表达式和方法引用之类的东西。

我的问题是,Java 是否正在使用一流的函数,或者这真的只是语法糖来减少实现匿名类/接口(如可运行)所需的代码量?(我的直觉说后者)。

我的理想场景

Map<String,DoubleToDoubleFunc> mymap = new HashMap<String,DoubleToDoubleFunc>();
...
mymap.put("Func 1", (double a, double b) -> a + b);
mymap.put("Func 2", Math::pow);
...
w = mymap.get("Func 1")(y,z);
x = mymap.get("Func 2")(y,z);
4

4 回答 4

7

Java 8中没有函数结构类型。但是 Java 一直都有一流的对象。事实上,它们已被用作替代品。从历史上看,由于缺乏一等函数,我们目前所做的就是将函数包装在一个对象中。这些是著名的 SAM 类型(单一抽象方法类型)。

所以,而不是

function run() {}
Thread t = new Thread(run);

我们的确是

Runnable run = new Runnable(){ public void run(){} };
Thread t = new Thread(run);

也就是说,我们将函数放在对象中,以便能够将其作为值传递。因此,长期以来,一流的对象一直是一种替代解决方案。

JDK 8 只是让实现这个概念变得更简单,他们将这种类型的包装器接口称为“功能接口”,并提供一些语法糖来实现包装器对象。

Runnable run = () -> {};
Thread t = new Thread(run);

但最终,我们仍然使用一流的对象。它们具有与一流函数相似的属性。它们封装了行为,可以作为参数传递并作为值返回。

lambda 邮件列表中,Brian Goetz 很好地解释了激发此设计的一些原因。

沿着我们今天讨论的路线,这里是我们前进的方向。我们探索了“也许 lambdas 应该只是内部类实例,那真的很简单”的道路,但最终走到了“函数是语言未来更好的方向”的位置。

这种探索分阶段进行:首先是在 EG 成立之前在内部进行,然后在 EG 讨论问题时再次进行。以下是我对这个问题的立场。希望这能填补规范当前所说的内容与我们所说的内容之间的一些空白。

关于 lambdas 是否是对象的问题在很大程度上归结为诸如“什么是真正的 lambdas”、“为什么 Java 将从 lambdas 中受益”以及最终“如何最好地发展 Java 语言和平台”之类的哲学问题。

Oracle 的立场是,Java 必须进化——当然要小心——才能保持竞争力。当然,这是一个困难的平衡行为。

我相信,发展 Java 的最佳方向是鼓励更实用的编程风格。Lambda 的作用主要是支持更多类函数库的开发和消费;我提供了诸如 filter-map-reduce 之类的示例来说明这个方向。

生态系统中有大量证据支持这样一个假设,即如果提供工具可以轻松做到这一点,那么面向对象的程序员就准备好接受函数式技术(例如不变性)并将它们转化为面向对象的世界观,因此会编写出更好、更不容易出错的代码。简而言之,我们相信我们能为 Java 开发人员做的最好的事情就是让他们温和地推动更实用的编程风格。我们不会将 Java 变成 Haskell,甚至不会变成 Scala。但方向很明确。

Lambda 是这种演变的首付,但远未结束。故事的其余部分还没有写完,但保留我们的选择是我们如何评估我们在这里做出的决定的一个关键方面。

这就是为什么我一直坚持认为 lambda 不是对象的原因。我相信“lambdas 只是对象”的立场,虽然非常舒适和诱人,但在语言进化的许多潜在有用的方向上关上了大门。

作为一个例子,让我们以函数类型为例。devoxx 提供的 lambda 稻草人具有函数类型。我坚持要删除它们,这让我不受欢迎。但我对函数类型的反对并不是因为我不喜欢函数类型——我喜欢函数类型——而是函数类型与 Java 类型系统的现有方面(擦除)发生了激烈的争执。擦除函数类型是两全其美的。所以我们从设计中删除了这个。

但我不愿意说“Java 永远不会有函数类型”(尽管我承认 Java 可能永远不会有函数类型。)我相信为了获得函数类型,我们必须首先处理擦除。这可能,也可能不可能。但在具体结构类型的世界中,函数类型开始变得更有意义。

lambdas-are-objects 的世界观与这个可能的未来相冲突。lambdas-are-functions 的世界观没有,保持这种灵活性是支持不给 lambdas 带来负担的要点之一,即使是对象性的外观。

您可能认为当前的设计与 lambda 的对象框(SAM 类型)紧密相关,从而使它们成为有效的对象。但这已经从表面区域小心地隐藏起来,以便我们将来考虑“裸” lambdas,或者考虑 lambdas 的其他转换上下文,或者将 lambdas 更紧密地集成到控制结构中。我们现在不这样做,甚至没有具体的计划,但未来可能这样做的能力是设计的关键部分。

我对 Java 的未来持乐观态度,但要向前迈进,我们有时不得不放弃一些自在的想法。Lambdas-are-functions 打开了大门。Lambdas-are-objects 关闭它们。我们更愿意看到那些门敞开着。

于 2014-03-26T16:23:14.390 回答
2

它们并不是真正的一流功能 IMO。Lambda 最终是一个功能接口的实例。但它确实为您提供了一流功能的优势。您可以将它作为参数传递给需要功能接口实例的方法。您可以将其分配给变量,在这种情况下,将使用目标类型推断其类型。您可以从方法中返回它,依此类推。

此外,lambdas 并没有完全取代匿名类。并非所有匿名类都可以转换为 lambda。您可以通过这个答案来很好地解释两者之间的区别。因此,任何 lambda 表达式都不是匿名类的语法糖。

于 2014-03-26T15:18:26.917 回答
1

它不是匿名类的语法糖;它编译成的内容涉及更多。(并不是说这样做是错误的;任何语言都有自己的 lambda 编译方式的实现细节,Java 并不比许多其他语言差。)

请参阅http://cr.openjdk.java.net/~briangoetz/lambda/lambda-translation.html了解完整的细节。

于 2014-03-26T15:13:57.460 回答
1
interface DoubleDoubleToDoubleFunc {
    double f(double x, double y);
}

public static void main(String[] args) {
    Map<String, DoubleDoubleToDoubleFunc> mymap = new HashMap<>();
    mymap.put("Func 1", (double a, double b) -> a + b);
    mymap.put("Func 2", Math::pow);
    double y = 2.0;
    double z = 3.0;
    double w = mymap.get("Func 1").f(y,z);
    double v = mymap.get("Func 2").f(y,z);
}

所以它仍然是语法糖,只是在某种程度上。

于 2014-03-26T15:20:40.683 回答