Java的lambda语法有什么细分?
请解释Java 8的lambda方法的语法。
有很多关于lambda函数的解释,但我找不到语法的详尽解释,我发现很难学会正确复制语法,因为我不明白为什么他们’按原样重写。
这是我遇到的常见案例,由NetBeans提供:
public static void main(String[] args) { SwingUtilities.invokeLater(() -> { new MainAppJFrame(); }); }
所以,不知何故,以下lambda表达式正在解析为匿名的Runnable
对象的run()方法:
() -> { // do stuff }
->
是lambda语法,对吗? 花括号只是包含匿名方法代码。 括号是否为空参数,因为在这种情况下我们正在创建一个Runnable.run()
方法?
这一点对我来说都不太清楚。 我假设编译器知道根据SwingUtilities.invokeLater(Runnable)
方法所期望的类型实例化一个匿名的Runnable
? 如果有两个SwingUtilities.invokeLater
方法只在参数列表中有所不同,会发生什么? 显然,在这种特定情况下没有,但在其他地方可能:
interface ExampleLambdaConsumer { public void doSomething(Runnable r); public void doSomething(java.lang.reflect.Method m); } class C implements ExampleLambdaConsumer { // implementations for doSomething methods here public static void main(String[] args) { doSomething(() -> { // lambda method body here } } }
语法是:
arguments -> body
arguments
可以是哪个
-
()
-
如果可以从上下文中推断出该变量的类型,则为单个变量
-
括号中包含或不包含类型的变量序列
和body可以是表达式,也可以是带有语句的{...}
块。 简单地返回表达式,即() -> 2
相当于() -> {return 2;}
编辑:如果lambda表达式像() -> f()
:
-
如果
f()
返回void
,则它们等效于() -> { f(); }
() -> { f(); }
-
否则,它们相当于
() -> { f(); }
() -> { f(); }
或() -> { return f(); })
() -> { return f(); })
。 编译器从调用上下文中推断出它,但通常它会更喜欢后者。
因此,如果您有两种方法: void handle(Supplier
和void handle(Runnable)
,则:
-
handle(() -> { return f(); })
和handle(() -> x)
将调用第一个, -
handle(() -> { f(); }
将调用第二个,和 -
handle(() -> f())
:-
如果
f()
返回void
或者不能转换为T
的类型,那么它将调用第二个 -
如果
f()
返回一个可转换为T
的类型,那么它将调用第一个类型
-
编译器尝试将lambda的类型与上下文匹配。 我不知道确切的规则,但答案是:
如果有两个SwingUtilities.invokeLater方法只在参数列表中有所不同,会发生什么?
是:它取决于那些参数列表。 如果另一个invokeLater
也只有一个参数,并且该参数的类型也是一个类型为void*()
方法的接口,那么它会抱怨它无法找出你的意思。
他们为什么这样写? 好吧,我认为这是因为C#和Scala中的语法几乎相同(它们使用=>
而不是->
)。
语法是
(parameter_list_here) -> { stuff_to_do; }
如果花括号是单个表达式,则可以省略花括号。 如果参数列表是单个参数,则可以省略参数列表周围的常规括号。
该语法仅适用于所有function接口。 @FunctionalInterface注释告诉编译器您打算编写这样的接口并在不满足要求时给出编译错误 – 例如,它必须只有1个可重写的方法。
@FunctionalInterface interface TestInterface { void dostuff(); }
Runnable也是这样宣布的。 其他接口不是,它们不能与lambda函数一起使用。
现在我们已经使用不带参数的方法创建了一个新的function接口,我们如何测试签名中有关“碰撞”的问题?
public class Main { private void test(Runnable r) { } private void test(TestInterface ti) { } public static void main(String[] args) { test(() -> { System.out.println("test");}) } @FunctionalInterface interface TestInterface { void dostuff(); } }
结果:编译错误:对方法测试进行严格调用。
您看,编译器/ VM(如果完成运行时)找到适当的方法及其参数列表,并查看参数是否是function接口,如果是,则创建该接口的匿名实现。 从技术上讲(在字节代码中)它与匿名类不同,但在其他方面相同(您将看不到Main $ 1.class文件)。
您的示例代码(由Netbeans提供)也可以替换为
SwingUtilities.invokeLater(MainAppJFrame::new);
顺便说一句。 🙂
Lambda表达式基本上在Java 8中采用,以简化作为匿名函数的覆盖进程函数 。
它们只是覆盖旧java匿名函数的捷径。
请参考以下示例:
假设您有接口A ,它只有一个声明如下的方法:
interface A{ void print(); }
现在使用旧的java样式,我们将以匿名的方式覆盖它,如下所示:
new A() { @Override public void print() { System.out.println("in a print method"); } };
另外现在使用java 8 lambda表达式我们将使用它如下:
() -> System.out.println("in a print method");
这里我们可以在->
运算符之前传递方法所需的参数,然后在->
运算符之后覆盖正文。
我们需要实现的唯一设置是我们需要使用@FunctionalInterface声明接口,如下所示:
@FunctionalInterface interface A{ void print(); }
注意: – lambda表达式只能用于只有一个非默认方法的“function”接口。