又是热爱学习的一天!!!
语法简介
Java8引入了一种新的操作符 “->”,该操作符叫做 箭头操作符 或者叫做 Lambda 操作符。箭头操作符将Lambda表达式分为了左右两部分
- 左侧:Lambda 表达式的参数列表。
- 右侧:Lambda 表达式所需执行的功能,也叫Lambda体。
上一篇 初识Lambda文章说道 ,Lambda表达式就是对接口的实现。以前是用匿名内部类去实现的接口,现在是用的Lambda表达式去实现的。
拿上篇文章的 StudentFilter 接口举例,接口中有个 filter 抽象方法。那么Lambda表达式的左侧就代表的 filter 方法的参数列表。Lambda表达式的右侧就是这个方法具体实现的功能
这里有一个疑问:Lambda 表达式自动的就找到了StudentFilter 接口中的 filter 抽象方法,如果这个接口中有多个抽象方法呢?该怎么办?
这里引出一个新概念:函数式接口
函数式接口
所谓函数式接口就是指接口中只有一个抽象方法的接口,我们称为函数式接口。
可以使用注解:@FunctionalInterface 来修饰
如果使用了该注解,那么就可以检测这个接口是不是函数式接口,不写也可以,但是得人工保证函数式接口的规范。而 Lambda 表达式就必须依赖一个函数式接口!
因为函数式接口的抽象方法可能是多种多样,比如无参数无返回值、无参数有返回值、有参数无返回值和有参数有返回值等多种情况,那么就有对应的几种略微不同的写法。
语法格式
语法格式一:无参数无返回值
我们在前面说到,Lambda表达式需要函数式接口的支持,那这里我们是不是需要新创建一个接口,并且增加一个无参数无返回值的抽象方法呢?
其实不用,这种抽象方法其实在Java中已经有一个了,比如:Runnable 接口就是一个函数式接口,接口中的 run 方法就是 无参数无返回值的。
/**
* 语法格式一:无参数无返回值
* () -> System.out.println("Hello Lambda")
*/
@Test
public void grammar_1(){
// 原来匿名内部类的写法
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello Inner class");
}
};
r.run();
System.out.println("************昏 哥 线*************");
// 现在使用 Lambda 表达式的写法
Runnable runnable = () -> System.out.println("Hello Lambda");
runnable.run();
}
执行结果:
Hello Inner class
************昏 哥 线*************
Hello Lambda
这里需要提一点: 在 jdk 1.7及以前 如果一个局部类 引用了一个局部变量,那么这个变量 需要显示的声明成 final,在 1.8中就不用显示的去声明了,编译器默认给我们加上了,但是这个变量任然是 final 的,是不可以修改的,使用如下所示:
@Test
public void grammar_1(){
int num = 25; // jdk 1.7及以前,需要显示的声明成 final
// 原来匿名内部类的写法
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello Inner class" + num);
}
};
r.run();
System.out.println("************昏 哥 线*************");
// 现在使用 Lambda 表达式的写法
Runnable runnable = () -> System.out.println("Hello Lambda" + num);
runnable.run();
}
语法格式二:有一个参数 无返回值
有一个参数,无返回值的这个接口。 Java里面也有现成的,他就是:Consumer 接口,它里面的 accept 抽象方法就符合我们的要求:
/**
* 语法格式二:有一个参数 无返回值
* (x) -> System.out.println(x)
*
* 注意:如果只有一个参数,那么 Lambda表达式左侧小括号可以省略不写,但是建议还是写上。
*/
@Test
public void grammar_2(){
Consumer consumer = (x) -> System.out.println(x);
consumer.accept("你好,Lambda");
}
语法格式三:有两个及以上参数 有返回值,并且 Lambda体中有多条语句
这种接口Java里面也有!它就是:Comparator 接口,它里面的 compare 方法就符合我们的要求:
/**
* 语法格式三:有两个及以上参数 有返回值,并且 Lambda体中有多条语句,那么 Lambda 体需要使用大括号括起来。
*
* 注意:如果两个及以上参数 有返回值,但是 Lambda 体只有一条语句的话,那么 return 和 大括号可以省略不写。
*/
@Test
public void grammar_3(){
Comparator<Integer> comparator = (x, y) -> {
System.out.println("这是多条语句的 Lambda 体...");
return Integer.compare(x, y);
};
int result = comparator.compare(100, 200);
System.out.println("返回值为:"+result);
System.out.println("************昏 哥 线*************");
// 这是 Lambda 体只有一条语句的话,那么 return 和 大括号可以省略不写 的情况。
Comparator<Integer> c = (x, y) -> Integer.compare(x, y);
int result2 = c.compare(200, 500);
System.out.println("返回值为:"+result2);
}
回头看几个 Lambda 语句,会发现 小括号里面的参数都是没有数据类型的,但是编译运行都没有问题,这就说明了,Lambda表达式的参数列表的数据类型可以省略不写,因为 JVM 编译器可以通过上下文推断出数据类型,这个过程就叫做 类型推断
番外
在上面说到函数式接口中只能有一个抽象方法,但是 Comparator 接口中除去 default 修饰的方法之外,还有两个,分别是:compare 和 equals。那么它为啥还是属于函数式接口呢?
原因就是 equals 它是 Object的方法,他已经有了默认实现,所以他不算是接口中的抽象方法
从 类的注释来看,可以总结出以下三点:
- 一个函数式接口有且只有一个抽象方法。
- 默认方法不是抽象方法,因为它们已经实现了。
- 重写了超类Object类中任意一个public方法的方法并不算接口中的抽象方法。
所以说 Comparator 接口还是只有 compare 这一个抽象方法,所以他符合函数式接口的标准
简单示例
需求:对指定数字进行运算
先创建一个函数式接口
@FunctionalInterface
public interface ICalculation<T> {
int calc(T x, T y);
}
再写一个操作方法 operation,具体进行什么运算现在还不知道,我们对传入的参数 x 和参数 y 进行运算并返回结果。
public int operation(int x, int y, ICalculation<Integer> iCalculation){
return iCalculation.calc(x, y);
}
调用 操作方法 operation,传入实参,使用 Lambda 表达式实现接口中的抽象方法进行运算。下面演示了乘法、加法和减法运算。
@Test
public void lambdaGrammar(){
int operation = operation(10, 5, (x, y) -> x * y);
System.out.println("乘法运算结果:"+operation);
System.out.println("*******昏 哥 线************");
System.out.println("加法运算结果:"+operation(10, 5, (x, y) -> x + y));
System.out.println("*******昏 哥 线************");
System.out.println("减法运算结果:"+operation(10, 5, (x, y) -> x - y));
}
运行结果:
乘法运算结果:50
*******昏 哥 线************
加法运算结果:15
*******昏 哥 线************
减法运算结果:5
总结
Lambda 表达式的语法其实比较简单,重点是分清它左右侧分别是什么内容,它的左侧就对应着函数式接口中抽象方法的参数列表,在它的左侧是可以不用写数据类型的,是因为有类型推断机制。如果非要写上数据类型,那就必须将所有参数都加上数据类型才可以。
它的右侧就是 Lambda 体,就是我们具体实现的内容,如果有多条语句需要使用大括号,只有一条语句可以省略大括号不写。
技 术 无 他, 唯 有 熟 尔。
知 其 然, 也 知 其 所 以 然。
踏 实 一 些, 不 要 着 急, 你 想 要 的 岁 月 都 会 给 你。