为什么引入Lambda表达式?
lambda概念来自于数学。λ演算,λ(Lambda(大写Λ,小写λ)读音:lan b(m) da(兰亩达)[‘læ;mdə])演算是一套用于研究函数定义、函数应用和递归的形式系统。其本质就是一个函数。
由数学界发展而来,成为编程届的一种思想,叫做函数式编程思想。每一个功能用一个函数去实现,函数优先,区别于面向对象思想。好处是可以简化代码,不需要先创建一个对象,再给对象创建方法。直接创建方法即可使用,尤其是在UI操作,是件回调等操作时,十分便利。
由于大部分语言都已经支持函数式编程,只有Java还紧抱面向对象,所以Java8开始支持函数式编程。
表现在代码中就是我们平时说的: 闭包 /匿名方法 /匿名函数,可以将一段代码直接作为参数传递,简化代码。
在Java中,Lambda的本质就是一个interface的匿名内部类的实现。Java8只是优化了语法糖。
快速上手
来个例子:
(x,y)->{return x+y;}
- 基本格式: (参数列表)->{方法体}
- ->符号代表了lambda表达式,读作λ(lambda 读音:兰木达)
- 示例:(x,y)->{System.out.println(x+y);}
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
- 以上lambda表达式还可以简化,jdk8还提供一种更简单的语法糖——方法引用。
- 用双冒号:: 表示方法引用。例如:Person::getName 等同于new Person().getName()
简单几个例子
无参数,无返回值 ()->{}
Runnable runnable = ()-> System.out.println("runnable方法执行");runnable.run();//等同于Runnable run = new Runnable() {@Overridepublic void run() {System.out.println("runnable方法执行");}};run.run();
有一个参数,无返回值
Consumer<String> consumer1 =(x)-> {System.out.println(x);};Consumer<String> consumer2 =(x)-> System.out.println(x);//只有一条语句 右大括号可以不写Consumer<String> consumer3 =x-> System.out.println(x);// 只有一个参数 左大括号可以不写Consumer<String> consumer4 = System.out::println;//可以简写为方法引用consumer4.accept("执行");//等同于Consumer<String> consumer5 = new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}};consumer5.accept("执行");
有两个参数以上,方法体有多条语句
Comparator<Integer> comparator = (x,y)->{System.out.println(x);System.out.println(y);return Integer.compare(x,y);};int compare = comparator.compare(1, 3);System.out.println(compare);//等同于以下写法Comparator<Integer> comparator1 = new Comparator<Integer>() {@Overridepublic int compare(Integer x, Integer y) {System.out.println(x);System.out.println(y);return Integer.compare(x,y);}};int compare1 = comparator1.compare(1, 3);System.out.println(compare);
需要注意的地方
//jdk自动识别类型 自动识别x为IntegerConsumer<Integer> consumer = x-> System.out.println(x);//相类似的还有集合Set<Integer> set = new HashSet<>();//此处<>自动识别为Integer类型//lambda传参默认是final的,因为jdk7中interface只能接受常量,这里需要兼容以前的jdkint x =1;//final int x= 1;//等同于Function f = ()-> {//x= 2;// 报错:Variable used in lambda expression should be final or effectively finalSystem.out.println(x);};
例子:比较两个数字大小
package com.demo.lambda表达式;import org.junit.Test;import java.util.Comparator;import java.util.TreeSet;/*** lambda表达式示例*/public class LambdaDemo {@Testpublic void jdk7() {//自定义一个数字比较器 实现Comparator接口Comparator<Integer> comparator = new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return Integer.compare(o1, o2);}};//自定义的比较器排序TreeSet<Integer> treeSet = new TreeSet<>(comparator);}@Testpublic void jdk8() {//自定义一个数字比较器 实现Comparator接口Comparator<Integer> comparator = (o1, o2) -> Integer.compare(o1, o2);//自定义的比较器排序TreeSet<Integer> treeSet = new TreeSet<>(comparator);}@Testpublic void jdk8简化写法() {//自定义一个数字比较器 实现Comparator接口Comparator<Integer> comparator = Integer::compare;//自定义的比较器排序TreeSet<Integer> treeSet = new TreeSet<>(comparator);}@Testpublic void jdk8继续简化() {TreeSet<Integer> treeSet = new TreeSet<>(Integer::compare);}//这几种写法都相同,Lamdba表达式,仅仅是JDK给我们提供的一种语法糖,简化代码的写法}
自己实现一个Lambda表达式
@FunctionalInterfacepublic interface Fun {void fun();}//测试接口使用public class FunTest {public static void main(String[] args) {Fun fun = ()->{System.out.println("自定义fun");System.out.println("自定义fun");};fun.fun();//等同于Fun fun1 = new Fun() {@Overridepublic void fun() {System.out.println("自定义fun");System.out.println("自定义fun");}};fun.fun();}}
- @FunctionalInterface修饰的表示这个interface为lambda接口,注解标注的只能有一个抽象方法,目的是实现Lambda表达式的同时兼容以前的jdk版本
- 另外接口还可以支持静态方法和默认方法,如下
@FunctionalInterfacepublic interface Fun {void fun();//@FunctionalInterface 注解标注的只能有一个抽象方法,目的是实现Lambda表达式的同时兼容以前的jdk版本//Multiple non-overriding abstract methods found in interface com.demo.lambda表达式.Function// void function1();//静态方法static Integer add(Integer x,Integer y){return x+y;}//默认方法default Integer def(Integer x,Integer y){return x+y;}}//方法调用Integer def = fun.def(1, 3);Integer add = Fun.add(1, 2);
集合框架常用的Lamdba方法
//集合排序List<Integer> list = Arrays.asList(1,2,4,6,7,3,4,2);list.sort(Integer::compareTo);list.forEach(System.out::print);
jdk提供的常用函数式接口
package com.initit.java8.lambda表达式;import org.junit.Test;import java.util.ArrayList;import java.util.Arrays;import java.util.List;import java.util.function.Consumer;import java.util.function.Function;import java.util.function.Predicate;import java.util.function.Supplier;/** Java8 内置的四大核心函数式接口** Consumer<T> : 消费型接口* void accept(T t);** Supplier<T> : 供给型接口* T get();** Function<T, R> : 函数型接口* R apply(T t);** Predicate<T> : 断言型接口* boolean test(T t);**/public class jdk内置的函数式接口 {//Consumer<T> 消费型接口 :@Testpublic void test1() {happy(10000, (m) -> System.out.println("每次消费:" + m + "元"));}public void happy(double money, Consumer<Double> con) {con.accept(money);}//Supplier<T> 供给型接口 :@Testpublic void test2() {List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));for (Integer num : numList) {System.out.println(num);}}//需求:产生指定个数的整数,并放入集合中public List<Integer> getNumList(int num, Supplier<Integer> sup) {List<Integer> list = new ArrayList<>();for (int i = 0; i < num; i++) {Integer n = sup.get();list.add(n);}return list;}//Function<T, R> 函数型接口:@Testpublic void test3() {String newStr = strHandler("\t\t\t 函数型接口 ", String::trim);System.out.println(newStr);String subStr = strHandler("函数型接口", (str) -> str.substring(2, 5));System.out.println(subStr);}//需求:用于处理字符串public String strHandler(String str, Function<String, String> fun) {return fun.apply(str);}//Predicate<T> 断言型接口:@Testpublic void test4() {List<String> list = Arrays.asList("Hello", "aa", "Lambda", "www", "ok");List<String> strList = filterStr(list, (s) -> s.length() > 3);for (String str : strList) {System.out.println(str);}}//需求:将满足条件的字符串,放入集合中public List<String> filterStr(List<String> list, Predicate<String> pre) {List<String> strList = new ArrayList<>();for (String str : list) {if (pre.test(str)) {strList.add(str);}}return strList;}}
常见的其他接口可以参考官方文档: https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
参数引用
方法引用
若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用(可以将方法引用理解为 Lambda 表达式的另外一种表现形式)
- 对象的引用 :: 实例方法名
- 类名 :: 静态方法名
- 类名 :: 实例方法名
__注意:
- 方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致
- 若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName
构造器引用
构造器的参数列表,需要与函数式接口中参数列表保持一致
package com.initit.java8.lambda表达式;import org.junit.Test;import java.io.PrintStream;import java.util.Comparator;import java.util.function.*;/** 一、方法引用:若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用* (可以将方法引用理解为 Lambda 表达式的另外一种表现形式)** 1. 对象的引用 :: 实例方法名** 2. 类名 :: 静态方法名** 3. 类名 :: 实例方法名** 注意:* ①方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!* ②若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName** 二、构造器引用 :构造器的参数列表,需要与函数式接口中参数列表保持一致!** 1. 类名 :: new** 三、数组引用** 类型[] :: new;***/public class 方法引用 {//数组引用@Testpublic void test8() {Function<Integer, String[]> fun = (args) -> new String[args];String[] strs = fun.apply(10);System.out.println(strs.length);System.out.println("--------------------------");Function<Integer, Employee[]> fun2 = Employee[]::new;Employee[] emps = fun2.apply(20);System.out.println(emps.length);}//构造器引用@Testpublic void test7() {Function<String, Employee> fun = Employee::new;BiFunction<String, Integer, Employee> fun2 = Employee::new;}@Testpublic void test6() {Supplier<Employee> sup = () -> new Employee();System.out.println(sup.get());System.out.println("------------------------------------");Supplier<Employee> sup2 = Employee::new;System.out.println(sup2.get());}//类名 :: 实例方法名@Testpublic void test5() {BiPredicate<String, String> bp = (x, y) -> x.equals(y);System.out.println(bp.test("abcde", "abcde"));System.out.println("-----------------------------------------");BiPredicate<String, String> bp2 = String::equals;System.out.println(bp2.test("abc", "abc"));System.out.println("-----------------------------------------");Function<Employee, String> fun = (e) -> e.show();System.out.println(fun.apply(new Employee()));System.out.println("-----------------------------------------");Function<Employee, String> fun2 = Employee::show;System.out.println(fun2.apply(new Employee()));}//类名 :: 静态方法名@Testpublic void test4() {Comparator<Integer> com = (x, y) -> Integer.compare(x, y);System.out.println("-------------------------------------");Comparator<Integer> com2 = Integer::compare;}@Testpublic void test3() {BiFunction<Double, Double, Double> fun = (x, y) -> Math.max(x, y);System.out.println(fun.apply(1.5, 22.2));System.out.println("--------------------------------------------------");BiFunction<Double, Double, Double> fun2 = Math::max;System.out.println(fun2.apply(1.2, 1.5));}//对象的引用 :: 实例方法名@Testpublic void test2() {Employee emp = new Employee(101, "张三", 18, 9999.99);Supplier<String> sup = () -> emp.getName();System.out.println(sup.get());System.out.println("----------------------------------");Supplier<String> sup2 = emp::getName;System.out.println(sup2.get());}@Testpublic void test1() {PrintStream ps = System.out;Consumer<String> con = (str) -> ps.println(str);con.accept("Hello World!");System.out.println("--------------------------------");Consumer<String> con2 = ps::println;con2.accept("Hello Java8!");Consumer<String> con3 = System.out::println;}class Employee {private int id;private String name;private int age;private double salary;public Employee() {}public Employee(String name) {this.name = name;}public Employee(String name, int age) {this.name = name;this.age = age;}public Employee(int id, String name, int age, double salary) {this.id = id;this.name = name;this.age = age;this.salary = salary;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public String show() {return "测试方法引用!";}@Overridepublic int hashCode() {final int prime = 31;int result = 1;result = prime * result + age;result = prime * result + id;result = prime * result + ((name == null) ? 0 : name.hashCode());long temp;temp = Double.doubleToLongBits(salary);result = prime * result + (int) (temp ^ (temp >>> 32));return result;}@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Employee other = (Employee) obj;if (age != other.age)return false;if (id != other.id)return false;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))return false;return true;}@Overridepublic String toString() {return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";}}}
