Lambda表达式
在Java中,函数(方法)是无法独立存在的,Java 一直都致力维护其对象至上的特征
不像JavaScript 之类的函数式编程语言,在函数式编程的语言中,你可以将函数赋给
一个变量或者将它作为另一个方法的参数来传递。
函数式编程带来的好处就是---闭包(关于闭包的解释参考文章https://zhuanlan.zhihu.com/p/22486908?refer=study-fe)
在Java中缺少了闭包的特性,但在Java8中的新特性 Lambda为Java添加缺失的函数式编程语言的特点
Lambda简介
Lambda表达式也是一种“匿名函数”,其没有方法名,返回值声明和修饰符
它允许在java中将方法作为参数 传递给其他方法
当某个方法只需要使用一次,定义很简短,你就可以使用它来代替,以省去不必要的代码
(但是Lambda表达式非常影响代码的可读性)
//以下部分内容为摘抄文章 地址:http://blog.oneapm.com/apm-tech/226.html 深入浅出 Java 8 Lambda 表达式
Java 中的 Lambda 表达式通常使用 (argument) -> (body)
语法书写,例如:
(arg1, arg2...) -> { body }
(type1 arg1, type2 arg2...) -> { body }
以下是一些 Lambda 表达式的例子:
(int a, int b) -> { return a + b; }
() -> System.out.println("Hello World");
(String s) -> { System.out.println(s); }
() -> 42
() -> { return 3.1415 };
Lambda 表达式的结构
让我们了解一下 Lambda 表达式的结构。
- 一个 Lambda 表达式可以有零个或多个参数
- 参数的类型既可以明确声明,也可以根据上下文来推断。例如:
(int a)
与(a)
效果相同 - 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:
(a, b)
或(int a, int b)
或(String a, int b, float c)
- 空圆括号代表参数集为空。例如:
() -> 42
- 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:
a -> return a*a
- Lambda 表达式的主体可包含零条或多条语句
- 如果 Lambda 表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致
- 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空
函数式接口
在进入正式的Lambda实例之前,我们需要了解Java8的另一个新特性: 函数式接口
什么是函数式接口? 函数式接口就是有且只有一个抽象方法但是可以有多个非抽象方法的接口
该接口可以被隐式的转换成Lambda表达式
如 Runnable 接口就是一个函数式接口(在其内部只有一个抽象方法为 void run() )
package java.lang;
/**
* 此处省略源码注释
*/
@FunctionalInterface
public interface Runnable {
/**
*此处省略源码注释
*/
public abstract void run();
}
我们可以看到Runnable类包含了一个 @FunctionalInterface 的注解 该注解的含义稍后再说
接下来看这个例子:
public class Demo{
public static void main(String[] args){
Runnable r = new Runnable(){
@Override
public void run(){
//代码块
}
}
}
}
在这部分代码中,是使用匿名内部类来实现的,在Java8之前,我们就是这样做的
但是在有了Lambda表达式后,我们可以换成另一种方式来实现
Runnable r = () -> System.out.println("Lambda");
每个 Lambda 表达式都能隐式地赋值给函数式接口,所以我们可以采用Lambda表达式来简化匿名内部类的方式
但同时它又一定程度上降低了代码的可读性
那么如果接口中包含了多个抽象方法时会怎么样呢?
为了验证,我们换一个有多个抽象方法接口做实验
public class Demo {
public static void main(String[] args) {
LambdaDemo d = () -> System.out.println("Demo");
}
}
interface LambdaDemo{
void methodOne();
void methodSec();
}
当然,编译器给我们报了一个异常信息:
错误:(5, 24) java: 不兼容的类型: com.java.lambda.LambdaDemo 不是函数接口
在 接口 com.java.lambda.LambdaDemo 中找到多个非覆盖抽象方法
根据异常信息,我们很快就能了解 函数式接口的定义是什么了
那么刚刚说到的@FunctionalInterface是什么呢
@FunctionalInterface
@FunctionalInterface该注解是Java8中新加入的接口,用于指明该接口类型声明是根据 Java 语言规范定义的函数式接口。
Java 8 还声明了一些 Lambda 表达式可以使用的函数式接口,当你注释的接口不是有效的函数式接口时,
可以使用 @FunctionalInterface 解决编译层面的错误。
@FunctionalInterface
interface LambdaDemo{
void methodOne();
void methodSec();
}
编译上述代码编译器会抛出一个异常:
错误:(9, 1) java: 意外的 @FunctionalInterface 注释
com.java.lambda.LambdaDemo 不是函数接口
在 接口 com.java.lambda.LambdaDemo 中找到多个非覆盖抽象方法
Lambda 表达式举例
学习 Lambda 表达式的最好方式是学习例子。
线程可以通过以下方法初始化:
//旧方法:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello from thread");
}
}).start();
//新方法:
new Thread(
() -> System.out.println("Hello from thread")
).start();
Lambda 表达式与匿名类的区别
使用匿名类与 Lambda 表达式的一大区别在于关键词的使用。对于匿名类,关键词 this
解读为匿名类,而对于 Lambda 表达式,关键词 this
解读为写就 Lambda 的外部类。
Lambda 表达式与匿名类的另一不同在于两者的编译方法。Java 编译器编译 Lambda 表达式并将他们转化为类里面的私有函数,它使用 Java 7 中新加的 invokedynamic
指令动态绑定该方法
//文章中的摘抄都做了链接