Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
Lambda是数学中的一个函数. Java中使用方法来代替函数,方法总是作为类或对象的一部分存在的。
可以把Lambda看作是一个匿名方法, 拥有更简洁的语法。
语法:
(参数列表) -> {语句;}
Lambda表达式由参数列表和一个Lambda体组成, 通过箭头连接。
说明:
● 当只有一个参数时, 参数列表的小括弧可以省略:x -> { System.out.println(x); }
● 参数列表中参数的数据类型可以省略:( x,y ) -> {x.compareTo(y);}
● 如果Lambda体只有一条语句,大括弧也可以省略:x -> { return x + 2 ; }
● 如果Lambda体中只有一条return语句, return关键字可以省略:(x,y) -> x+y
以下表达式是有效的Lambda表达式
(String s ) -> s.length()
(Student stu) -> stu.getAge() > 18
(int x, int y ) -> {
System.out.print(“result”);
System.out.println( x + y );
}
() -> {}
() -> “hehe”
(String s) -> { “wkcto” } //不合法
(String s) -> { return “wkcto” ; }
(String s) -> “wkcto”
Lambda使用案例:
布尔表达式, 判断参数接收的list集合是否为空
(List list) -> list.isEmpty()
创建对象,并返回
() -> new Student()
消费(使用)一个对象, 把参数接收学生对象的姓名打印出来
(Student stu ) -> { System.out.println( stu.name) ; }
从一个对象中选择, 返回参数对象的成绩
(Student stu) -> stu.getScore()
组合两个值
(int a, int b) -> a*b
比较两个对象
(Student stu1, Student stu2) -> stu1.getScore() - stu2.getScore()
在JDK8中, 引用了函数式接口. 就是只定义一个抽象方法的接口.如:Comparator接口 , Runnable接口
@FunctionalInterface //注解,声明接口为函数式接口
public interface Adder{
int add(int x, int y);
}
public interface ByteAdder extends Adder{
byte add( byte b1, byte b2);
}
当前ByteAdder接口不是函数式接口, 从Adder接口中继承了一个抽象方法,在本接口中又定义了一个抽象方法
public inteface Nothing{
}
当前Nothing接口也不函数式接口,因为没有抽象方法
函数式接口就是为Lambda表达式准备的,或者说Lambda表达式必须实现一个函数式接口
java.util.function包中定义了一些基本的函数式接口,如Predicate, Consumer, Function,Supplier等
Predicate接口中定义一个抽象方法test(T ),接收一个T类型的对象参数,返回一个布尔值
当需要一个涉及类型T的布尔表达式时,可以使用这个接口
public class Test01 {
public static void main(String[] args) {
List list = Arrays.asList("lisi", "zhangsan", "wangwu", "chenqi", "feifei");
List result = filter(list, x -> x.length() > 6);
System.out.println(result);
}
//定义方法, 方法可以把List列表中符合条件的元素存储到一个新的List列表中返回
public static List filter(List list, Predicate predicate){
List result = new ArrayList<>();
//遍历list参数列表,把符合predicate条件的元素存储到result中
for( T t : list){
if( predicate.test(t)){
result.add(t);
}
}
return result;
}
}
2、Consumer接口
Consumer接口定义了一个accept(T)抽象方法,可以接收一个T类型的对象,没有返回值. 如果需要访问类型T的对象,对该对象做一些操作,就可以使用这个接口. 在Collection集合和Map集合中都有forEach(Consumer)方法
public class Test02Consumer {
public static void main(String[] args) {
List list =Arrays.asList("lisi", "gg", "jj", "tuantuan", "daimeir", "timo","XDD");
list.forEach(s -> System.out.println(s) );
Map map = new HashMap<>();
map.put("lisi", 22);
map.put("feifei", 28);
map.put("zhangxiaosan", 20);
map.put("chenqi", 30);
map.forEach((k,v)-> System.out.println(k + "->" + v) );
}
}
3、Function接口
Function接口中定义了accept( T )方法,接收一个T类型的参数对象,返回一个R类型的数据. 如果需要定义一个Lambda,将一个输入对象的信息加工后映射到输出,就可以使用该接口
public class Test03Function {
public static void main(String[] args) {
List list = Arrays.asList("lisi", "gg", "jj", "tuantuan", "daimeir", "timo","XDD");
//把list集合中存储字符串的长度映射出来
List result = map(list, x->x.length() );
System.out.println(result);
}
//把一个List列表映射到另外一个List列表中
public static List map(List list, Function function){
List result = new ArrayList<>(); //存储映射后的数据
for( T t : list){
result.add( function.apply(t) ); //把apply对t对象的操作结果保存到result集合中
}
return result;
}
}
4、原始类型的处理
泛型只能绑定引用类型,不能使用基本类型。
Java8中函数式接口为基本类型也提供了对应的接口,可以避免在进行输入输出原始数据时频繁进行装箱/拆箱操作。
一般来说,在针对专门的基本类型数据的函数式接口名称前面加上了对应的原始类型前缀,如: IntPredicate, IntConsumer, IntFunction等。
IntPredicate evenNumbers = (int x ) -> x % 2 == 0 ;
evenNumbers.test( 10 ); //返回true
evenNumbers.test( 11 ); //返回false
Lambda表达式捕获
Lambda表达式可以使用外层作用域中定义的变量,如成员变量,局部变量,称为捕获Lambda。
package com.wkcto.lambda;
import java.util.function.IntUnaryOperator;
/**
* author: 极悦老崔
* 2019/3/13
*/
public class Test04LambdaVariable {
int xx = 123; //实例变量
static int yy = 456; //静态变量
public static void main(String[] args) {
//在Lambda表达式中使用成员变量,当前main方法是静态方法,只能使用静态变量
IntUnaryOperator operator = i ->{
return i + yy; //把参数接收的数据与静态变量的值相加并返回
};
System.out.println( operator.applyAsInt(10));
//在Lambda表达式中使用局部变量,局部变量必须是final修饰,或者是事实上的final
int zz = 789; //局部变量
final int ff = 147; //final修饰的局部变量
IntUnaryOperator operator2 = i->{
return i + ff; //把参数接收的数据与ff的值相加,然后返回
};
System.out.println(operator2.applyAsInt(10));
operator2 = i ->{
return i + zz; //zz虽然没有使用final修饰,如果它是事实的final,后面没有修改zz值的代码
};
System.out.println( operator2.applyAsInt(10));
// zz = 258; //如果再对zz重新赋值,则上面的lambda表达式语法错误
}
}