前言:Lambda 是一个匿名函数,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升。
首先我们来看下一个简单从匿名类到Lambda的例子,体会下Lambda的特点
在java8以前我们通过实行Runable接口创建线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello Runnable Run");
}
}).start();
利用java8的Lambad语法我们可以更方便的实现相同的功能
new Thread(() -> System.out.println("Hello Runnable Run")).start();
例2:在这里我们定义一个需求:通过年龄或者工资过滤员工信息
基于此业务场景我们先定义一个员工对象
package cq.java8.lambda;
public class Employ {
private Integer age; //年龄
private Double salary; //工资
private String name; //姓名
public Employ() {
super();
}
public Employ(Integer age, Double salary, String name) {
super();
this.age = age;
this.salary = salary;
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Employ [age=" + age + ", salary=" + salary + ", name=" + name + "]";
}
}
我们简单运用策略模式实现此类业务需求,首先定义一个公共的接口
//定义一个接口
public interface EmployStrategy {
//定义一个筛选方法
public boolean filterEmploy(Employ employ);
}
实现上面的公共接口过滤年龄大于25岁的员工
//定义一个根据年龄筛选的实现类
public class EmployFilterByAge implements EmployStrategy{
@Override
public boolean filterEmploy(Employ employ) {
return employ.getAge() > 25 ? true : false;
}
}
实现上面的公共接口实现年龄工资大于6000的员工
//定义一个根据工资筛选的实现类
public class EmployFilterBySalary implements EmployStrategy{
@Override
public boolean filterEmploy(Employ employ) {
return employ.getSalary() > 6000 ? true : false;
}
}
功能实现
public class TestLambda {
List employs = Arrays.asList(
new Employ(18, 5555.55, "张三"),
new Employ(22, 6666.66, "李四"),
new Employ(33, 3333.33, "王五"),
new Employ(44, 9999.99, "赵六"),
new Employ(55, 8888.88, "田七"));
//定义过滤员工返回新员工集合方法
public List getEmployByFilter(List employs, EmployStrategy employStrategy){
List newEmploys = new ArrayList();
for(Employ employ : employs) {
if(employStrategy.filterEmploy(employ)) {
newEmploys.add(employ);
}
}
return newEmploys;
}
//通过策略模式实现
@Test
public void test() {
//根据年龄过滤
List ageFilterEmploys = getEmployByFilter(employs, new EmployFilterByAge());
System.out.println("年龄大于25的员工:");
for (Employ e : ageFilterEmploys) {
System.out.println(e);
}
//根据工资过滤
List salaryFilterEmploys = getEmployByFilter(employs, new EmployFilterBySalary());
System.out.println("工资大于6000的员工:");
for (Employ e : salaryFilterEmploys) {
System.out.println(e);
}
}
}
运行结果如下:
年龄大于25的员工:
Employ [age=33, salary=3333.33, name=王五]
Employ [age=44, salary=9999.99, name=赵六]
Employ [age=55, salary=8888.88, name=田七]
工资大于6000的员工:
Employ [age=22, salary=6666.66, name=李四]
Employ [age=44, salary=9999.99, name=赵六]
Employ [age=55, salary=8888.88, name=田七]
此实现方式进行了解耦,弊端就是每次新增需求需要加新的接口实现。当然我们也可以用匿名内部类的方式实现,当然我们也可以通过匿名内部类的方式实现,如下:
//通过匿名内部类实现
@Test
public void test2() {
//根据年龄过滤
List ageFilterEmploys = getEmployByFilter(employs, new EmployStrategy() {
@Override
public boolean filterEmploy(Employ employ) {
return employ.getAge() > 25 ? true : false;
}
});
System.out.println("年龄大于25的员工:");
for (Employ e : ageFilterEmploys) {
System.out.println(e);
}
//根据年龄过滤
List salaryFilterEmploys = getEmployByFilter(employs, new EmployStrategy() {
@Override
public boolean filterEmploy(Employ employ) {
return employ.getSalary() > 6000 ? true : false;
}
});
System.out.println("工资大于6000的员工:");
for (Employ e : salaryFilterEmploys) {
System.out.println(e);
}
}
从第一个创建线程的例子中,延用相同的思路我们利用java8的lambda语法实现上述功能如下(接口还是使用EmployStrategy)
//Lambda表达式实现
@Test
public void test3() {
List ageFilterEmploys = getEmployByFilter(employs, e -> e.getAge() > 25);
System.out.println("年龄大于25的员工:");
for (Employ e : ageFilterEmploys) {
System.out.println(e);
}
List salaryFilterEmploys = getEmployByFilter(employs, e -> e.getSalary() > 6000);
System.out.println("年龄大于25的员工:");
for (Employ e : salaryFilterEmploys) {
System.out.println(e);
}
}
从上文实现员工过滤的实例中我们可以看到Lambda表单式可以大大简化代码。在这里我们也可以对Lambda表达式有个简单的了解和定义:可以将lambda表达式定义为一种 简洁、可传递的匿名函数,首先我们需要明确lambda表达式本质上是一个函数,虽然它不属于某个特定的类,但具备参数列表、函数主体、返回类型,以及能够抛出异常;其次它是匿名的,lambda表达式没有具体的函数名称;lambda表达式可以像参数一样进行传递,从而极大的简化代码的编写。上文所阐述的其实就是在告诉大家,我们为什么要使用Lambda表达式,接下来我们详细了解下Lambda语法。
1.表达式介绍
Lambda表达式在Java语言中引入了新的语法元素和操作符。这个操作符为 “ ->” , 该操作符被称为 Lambda 操作符或剪头操作符。它将 Lambda 分为两个部分:
左侧: 指定了 Lambda 表达式需要的所有参数
右侧: 指定了 Lambda 体,即 Lambda 表达式要执行的功能。
2.语法介绍
(2.1)语法格式一:无参,无返回值,Lambda体只需要一条语句
语法格式及案例:() -> System.out.println("无参无返回值Lambda表达式")
@Test
public void test4() {
new Thread(() -> System.out.println("无参无返回值Lambda表达式")).start();
}
(2.2)语法格式二:一个参数,无返回值,此时参数的小括号可省略
语法格式及案例:args -> System.out.println(args)
@Test
public void test5() {
Consumer con = x -> System.out.println(x);
con.accept("新年好");
}
(2.3)语法格式三:一个参数,有返回值,当Lambda体只有一条语句是return与大括号可省略
语法格式及案例:(args) -> return args参数处理后的结果
@Test
public void test6() {
//接收一个参数返回其2的倍数
Function fun = (x) -> 2*x;
fun.apply(5);
}
(2.4)语法格式四:两个参数,无返回值,当参数的数据类型可由编译器通过上下文推荐出来时,可省
语法格式及案例:(args1,args2) -> {两个参数处理,无返回值}
@Test
public void test7() {
//求两个数的和并打印
BiConsumer biConsumer = (x, y) -> System.out.println(x+y);
biConsumer.accept(10, 20);
}
(2.5)语法格式五:两个参数,有返回值,Lambda体只有1条语句
语法格式及案例:(args1, args2) -> return args1和args2处理后返回
@Test
public void test8() {
Comparator com = (x, y) -> Integer.compare(x, y);
int r = com.compare(5, 10);
System.out.println(r);
}
(2.6)语法格式五:两个参数,有返回值,Lambda体有多条语句
语法格式及案例:
(args1, args2) -> { System.out.println("输入两个参数"); return args1和args2处理后返回 }
@Test
public void test9() {
Comparator com = (x, y) ->{
System.out.println("输入两个参数");
return Integer.compare(x, y);
}
int r = com.compare(5, 10);
System.out.println(r);
}
3.JDK1.8提供的内置函数式接口
上文中我们定义了EmployStrategy 接口,并且只包含一个抽象方法的接口,可以称之为函数式接口,我们可以在任意的函数式接口上使用@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口,我们可以通过Lambda表达式创建该接口对象。
Java 内置四大核心函数式接口
接口类型 | 函数式接口 | 参数类型 | 返回值 | 用途 |
---|---|---|---|---|
消费型 | Consumer |
T | 无 | 对类型为T的对象应用操作,方法:void accept(T t) |
供给型 | Supplier |
无 | T | 返回类型为T的对象,方法:T get() |
函数型 | Function |
T | R | 对类型为T的应用操作,并返回结果。结果是R类型的对象,方法R apply(T t) |
断言型 | Predicate |
T | boolean | 确定对象T是否满足某个约束条件并返回boolean值,方法boolean test(T t) |
其他函数式接口
接口类型 | 函数式接口 | 参数类型 | 返回值 | 用途 |
---|---|---|---|---|
函数型 | BiFunction |
T,U | R | 对类型为T,U的对象应用操作,返回R类型的结果方法:R apply(T t,U u) |
函数型 | UnaryOperator |
T | T | 对类型为T的对象应用操作(一元运算),返回T类型的结果,方法:T apply(T t),是Function的子类 |
函数型 | BinaryOperator |
T,T | T | 对类型为T的应用操作(二元运算),并返回T类型结果。结果是R类型的对象,方法R apply(T t1, T t2),是BiFunction的子类 |
消费型 | BiConsumer |
T,U | 无 | 对类型为T,U的对象应用操作,方法void accept(T t, U u) |
在这里我们着重介绍四大核心函数式接口的使用
(3.1) Consumer
在这里我们定义一个需求:执行一段业务逻辑无参数无返回值,用此内置函数式接口实现如下:
public class TestConsumer {
//定义一个花钱的消费方法,参数:钱、消费函数接口
public void costMoney(double money, Consumer con) {
//花钱
con.accept(money);
}
@Test
public void test() {
//参数m,无返回值
costMoney(1000, m -> System.out.println("花了"+m+"元钱,很开心!"));
}
}
(3.2) Supplier
在这里我们定义一个需求作为实例:获取指定数量的随机数,并放入集合,用此内置函数式接口实现如下:
public class TestSupplier {
//获取指定数量的随机数,并放入集合返回
public List getRandList(int num, Supplier sup){
List randList = new ArrayList<>();
for(int i = 0; i < num; i++) {
//获取随机数并放入集合
randList.add(sup.get());
}
return randList;
}
@Test
public void test() {
List randList = getRandList(10, () -> (int)(Math.random()*100));
System.out.println(randList);
//打印结果:[76, 43, 23, 12, 42, 96, 84, 82, 4, 93]
}
}
(3.3) Function
在这里我们定义一个需求作为实例:去除字符串空格,用此内置函数式接口实现如下:
public class TestFunction {
//定义获取字符串方法
public String getStr(String string, Function fun) {
return fun.apply(string);
}
@Test
public void test() {
String string = " 中国人民万岁,中华人名共和国万岁";
//去掉空格
string = getStr(string, (t) -> t.trim());
System.out.println(string);
//打印结果:中国人民万岁,中华人名共和国万岁
}
}
(3.4) Predicate
在这里我们定义一个需求:从集合中获取部分满足条件的元素放入新集合并返回,用此内置函数式接口实现如下:
public class TestPredicate {
//从集合中获取部分满足条件的元素放入新集合并返回
public List getList(List list, Predicate pre){
List listNew = new ArrayList<>();
for(Integer v : list) {
if(pre.test(v)) {
listNew.add(v);
}
}
return listNew;
}
@Test
public void test() {
List list = Arrays.asList(39,8,12,99,22);
//获取值小于30,并返回新集合
List listNew = getList(list, (value) -> value < 30);
System.out.println("小于30:"+listNew);
//获取值大于40,并返回新集合
listNew = getList(list, value -> value > 40);
System.out.println("大于40:"+listNew);
//打印结果
//小于30:[8, 12, 22]
//大于40:[99]
}
}