其实lambda表达式的本质是接口的子实现
lambda表达式简化了匿名内部类的写法
lambda表达式另一个叫法,函数式编程
1.lambda表达式:
lambda表达式可以称之为闭包,他是java8中的一个非常重要的特性
很多java8新特性的写法都会用到lambda表达式
lambda表达式允许函数作为一个方法的参数,函数作为参数传递到方法中,这样代码就会变得简洁
语法:
lambda 操作符 ->
-> 左侧: 是 lambda 表达式的参数列表(接口中抽象方法的参数列表)
-> 右侧: 是 lambda 表达式中所需执行的功能,即 lambda 体(接口中抽象方法实现的功能)
1 | (parameters)->expression表达式 |
说明:
-可选参数声明:不需要声明参数类型,编译器统一识别参数值
-可选参数圆括号:只有一个参数无需定义圆括号,多个参数需要定义圆括号
-可选大括号:如果主体包含了一个语句,就不需要使用大括号,return 也可省
-可选返回关键字:如果主体只有一个表达式返回值,则编译器
会自动返回值,大括号需要指定返回了一个数据值
比如:
//不要参数,返回值为5
()->5
//接受一个参数(推断出数字类型),返回参数的2倍
x->2*x
//接受两个参数(推断出数字类型),返回值为参数差值
(x,y)->x-y
//接受两个参数,返回值为x-y
(int x,int y)->x-y
//接受一个参数,类型为String的参数,并输出参数的值
//推断出没有返回值,相当于方法的返回值void
(String str)->System.out.println(str)
//接受两个参数(推断出数字类型,返回值为参数的差值)
(x,y)->{return (x-y);}
其实lambda表达式的本质就是接口的子实现
lambda表达式简化了匿名内部类的写法
lambda表达式另一个写法叫做函数式编程
注意:
就是把接口的实现是内部类,转换成函数式写法
使用lambda表达式需要注意变量的作用域问题:
- lambda表达式引用外层的成员变量
如果外层的成员变量为final的,则在lambda内部不能修改成员变量的值
如果外层的成员变量不是final的,则在lambda内部能修改成员变量的值 - lambda表示式的局部变量可以不用声明为final,但必须不可以被后面的代码修改(即隐式的final特性)
- lambda表达式不允许声明一个与局部变量同名的参数或局部变量
2.方法的引用:
方法的引用方式用类名::方法名,其本质还是lambda表达式
具体的写法有四种
-构造器的引用 语法Class::new
-静态方法的引用 语法 Class::静态方法的名称
-特定类任意的对象引用 语法 Class::非静态方法名称
-特定对象的方法引用 语法 instance(实例)对象::非静态的方法的的名称
1 | public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{...} |
在 Iterable 接口的源码中提供了方法 forEach(Consumer<? super T> action) ,该方法的参数为一个接口 Consumer 对象, 并且调用其 accept(t) 方法。
1 | //Iterable接口的源码 |
在 Consumer 接口源码中,有一个 accept(T t) 方法。而且该接口用 @FunctionalInterface 修饰,是一个函数式接口
1 | //Consumer接口源码 |
再结合案例代码,不难分析
3.函数式接口
lambda 表达式需要”函数式接口”的支持
接口中只有一个抽象方法的接口,称为函数式接口,可以使用注解 @FunctionalInterface 修饰 (保证该接口中只有一个抽象方法),可以检查是否是函数式接口。
lambda 练习
- 调用 Collections.sort() 方法,通过定制排序比较两个 Employee (先按年龄比,年龄相同按姓名比), 使用 lambda 作为参数传递。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48package lambda;
import java.util.ArrayList;
import java.util.Collections;
/**
* @author gavino
*/
public class T_1 {
public static void main(String[] args) {
ArrayList<Employee> emps = new ArrayList<Employee>();
emps.add(new Employee("张三",19));
emps.add(new Employee("李四",18));
emps.add(new Employee("赵六",19));
emps.add(new Employee("王五",20));
Collections.sort(emps,(e1,e2)->{
if (e1.getAge()==e2.getAge()) {
return e1.getName().compareTo(e2.getName());
}else {
return Integer.compare(e1.getAge(), e2.getAge());
}
});
emps.forEach(emp -> System.out.println(emp));
}
}
class Employee{
private String name;
private Integer age;
public Employee() {
}
public Employee(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
@Override
public String toString() {
return "Employee [name=" + name + ", age=" + age + "]";
}
}
结果
1 | Employee [name=李四, age=18] |
① 声明函数式接口,接口中声明抽象方法,public string getValue(String str);
② 声明类 TestLambda ,类中编写方法使用接口作为参数,将一个字符串转换成大写,并作为方法的返回值
③ 再将字符串的第 2 个和第 4 个索引位置进行截取子串1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27package lanmbda;
public class T_2 {
public static void main(String[] args) {
String str = "lanzhou";
String subStr = strHandler(str,(s)->s.substring(2, 4));
System.out.println(subStr);
String upperStr = strHandler(str,(s)->s.toUpperCase());
System.out.println(upperStr);
}
/**
* 用于处理字符串
* @return
*/
public static String strHandler(String str, MyFunction mf) {
return mf.getValue(str);
}
}
/**
* 函数式接口
* @author gavino
*/
@FunctionalInterface
interface MyFunction{
public String getValue(String str);
}① 声明一个带两个泛型的函数式接口,泛型类型为 <T,R> T 为参数,R 为返回值
② 接口中声明对应抽象方法
③ 在 TestLambda 类中声明方法,使用接口作为参数,计算两个 long 型参数的和
④ 再计算两个 long 型参数的乘积
4.四大内置核心函数式接口
在 java 8 之前已经有部分的函数式接口
比如
java 8 以后

