为什么引入Lambda表达式?

lambda概念来自于数学。λ演算,λ(Lambda(大写Λ,小写λ)读音:lan b(m) da(兰亩达)[‘læ;mdə])演算是一套用于研究函数定义、函数应用和递归的形式系统。其本质就是一个函数。

由数学界发展而来,成为编程届的一种思想,叫做函数式编程思想。每一个功能用一个函数去实现,函数优先,区别于面向对象思想。好处是可以简化代码,不需要先创建一个对象,再给对象创建方法。直接创建方法即可使用,尤其是在UI操作,是件回调等操作时,十分便利。

由于大部分语言都已经支持函数式编程,只有Java还紧抱面向对象,所以Java8开始支持函数式编程。

表现在代码中就是我们平时说的: 闭包 /匿名方法 /匿名函数,可以将一段代码直接作为参数传递,简化代码。

在Java中,Lambda的本质就是一个interface的匿名内部类的实现。Java8只是优化了语法糖。

快速上手

来个例子:

  1. (x,y)->{return x+y;}
  • 基本格式: (参数列表)->{方法体}
  • ->符号代表了lambda表达式,读作λ(lambda 读音:兰木达)
  • 示例:(x,y)->{System.out.println(x+y);}
  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。
  • 以上lambda表达式还可以简化,jdk8还提供一种更简单的语法糖——方法引用
  • 用双冒号:: 表示方法引用。例如:Person::getName 等同于new Person().getName()

    简单几个例子

    无参数,无返回值 ()->{}
  1. Runnable runnable = ()-> System.out.println("runnable方法执行");
  2. runnable.run();
  3. //等同于
  4. Runnable run = new Runnable() {
  5. @Override
  6. public void run() {
  7. System.out.println("runnable方法执行");
  8. }
  9. };
  10. run.run();

有一个参数,无返回值

  1. Consumer<String> consumer1 =(x)-> {System.out.println(x);};
  2. Consumer<String> consumer2 =(x)-> System.out.println(x);//只有一条语句 右大括号可以不写
  3. Consumer<String> consumer3 =x-> System.out.println(x);// 只有一个参数 左大括号可以不写
  4. Consumer<String> consumer4 = System.out::println;//可以简写为方法引用
  5. consumer4.accept("执行");
  6. //等同于
  7. Consumer<String> consumer5 = new Consumer<String>() {
  8. @Override
  9. public void accept(String s) {
  10. System.out.println(s);
  11. }
  12. };
  13. consumer5.accept("执行");

有两个参数以上,方法体有多条语句

  1. Comparator<Integer> comparator = (x,y)->{
  2. System.out.println(x);
  3. System.out.println(y);
  4. return Integer.compare(x,y);
  5. };
  6. int compare = comparator.compare(1, 3);
  7. System.out.println(compare);
  8. //等同于以下写法
  9. Comparator<Integer> comparator1 = new Comparator<Integer>() {
  10. @Override
  11. public int compare(Integer x, Integer y) {
  12. System.out.println(x);
  13. System.out.println(y);
  14. return Integer.compare(x,y);
  15. }
  16. };
  17. int compare1 = comparator1.compare(1, 3);
  18. System.out.println(compare);

需要注意的地方

  1. //jdk自动识别类型 自动识别x为Integer
  2. Consumer<Integer> consumer = x-> System.out.println(x);
  3. //相类似的还有集合
  4. Set<Integer> set = new HashSet<>();//此处<>自动识别为Integer类型
  5. //lambda传参默认是final的,因为jdk7中interface只能接受常量,这里需要兼容以前的jdk
  6. int x =1;
  7. //final int x= 1;//等同于
  8. Function f = ()-> {
  9. //x= 2;// 报错:Variable used in lambda expression should be final or effectively final
  10. System.out.println(x);
  11. };

例子:比较两个数字大小

  1. package com.demo.lambda表达式;
  2. import org.junit.Test;
  3. import java.util.Comparator;
  4. import java.util.TreeSet;
  5. /**
  6. * lambda表达式示例
  7. */
  8. public class LambdaDemo {
  9. @Test
  10. public void jdk7() {
  11. //自定义一个数字比较器 实现Comparator接口
  12. Comparator<Integer> comparator = new Comparator<Integer>() {
  13. @Override
  14. public int compare(Integer o1, Integer o2) {
  15. return Integer.compare(o1, o2);
  16. }
  17. };
  18. //自定义的比较器排序
  19. TreeSet<Integer> treeSet = new TreeSet<>(comparator);
  20. }
  21. @Test
  22. public void jdk8() {
  23. //自定义一个数字比较器 实现Comparator接口
  24. Comparator<Integer> comparator = (o1, o2) -> Integer.compare(o1, o2);
  25. //自定义的比较器排序
  26. TreeSet<Integer> treeSet = new TreeSet<>(comparator);
  27. }
  28. @Test
  29. public void jdk8简化写法() {
  30. //自定义一个数字比较器 实现Comparator接口
  31. Comparator<Integer> comparator = Integer::compare;
  32. //自定义的比较器排序
  33. TreeSet<Integer> treeSet = new TreeSet<>(comparator);
  34. }
  35. @Test
  36. public void jdk8继续简化() {
  37. TreeSet<Integer> treeSet = new TreeSet<>(Integer::compare);
  38. }
  39. //这几种写法都相同,Lamdba表达式,仅仅是JDK给我们提供的一种语法糖,简化代码的写法
  40. }

自己实现一个Lambda表达式

  1. @FunctionalInterface
  2. public interface Fun {
  3. void fun();
  4. }
  5. //测试接口使用
  6. public class FunTest {
  7. public static void main(String[] args) {
  8. Fun fun = ()->{
  9. System.out.println("自定义fun");
  10. System.out.println("自定义fun");
  11. };
  12. fun.fun();
  13. //等同于
  14. Fun fun1 = new Fun() {
  15. @Override
  16. public void fun() {
  17. System.out.println("自定义fun");
  18. System.out.println("自定义fun");
  19. }
  20. };
  21. fun.fun();
  22. }
  23. }
  • @FunctionalInterface修饰的表示这个interface为lambda接口,注解标注的只能有一个抽象方法,目的是实现Lambda表达式的同时兼容以前的jdk版本
  • 另外接口还可以支持静态方法和默认方法,如下
  1. @FunctionalInterface
  2. public interface Fun {
  3. void fun();
  4. //@FunctionalInterface 注解标注的只能有一个抽象方法,目的是实现Lambda表达式的同时兼容以前的jdk版本
  5. //Multiple non-overriding abstract methods found in interface com.demo.lambda表达式.Function
  6. // void function1();
  7. //静态方法
  8. static Integer add(Integer x,Integer y){
  9. return x+y;
  10. }
  11. //默认方法
  12. default Integer def(Integer x,Integer y){
  13. return x+y;
  14. }
  15. }
  16. //方法调用
  17. Integer def = fun.def(1, 3);
  18. Integer add = Fun.add(1, 2);

集合框架常用的Lamdba方法

  1. //集合排序
  2. List<Integer> list = Arrays.asList(1,2,4,6,7,3,4,2);
  3. list.sort(Integer::compareTo);
  4. list.forEach(System.out::print);

jdk提供的常用函数式接口

  1. package com.initit.java8.lambda表达式;
  2. import org.junit.Test;
  3. import java.util.ArrayList;
  4. import java.util.Arrays;
  5. import java.util.List;
  6. import java.util.function.Consumer;
  7. import java.util.function.Function;
  8. import java.util.function.Predicate;
  9. import java.util.function.Supplier;
  10. /*
  11. * Java8 内置的四大核心函数式接口
  12. *
  13. * Consumer<T> : 消费型接口
  14. * void accept(T t);
  15. *
  16. * Supplier<T> : 供给型接口
  17. * T get();
  18. *
  19. * Function<T, R> : 函数型接口
  20. * R apply(T t);
  21. *
  22. * Predicate<T> : 断言型接口
  23. * boolean test(T t);
  24. *
  25. */
  26. public class jdk内置的函数式接口 {
  27. //Consumer<T> 消费型接口 :
  28. @Test
  29. public void test1() {
  30. happy(10000, (m) -> System.out.println("每次消费:" + m + "元"));
  31. }
  32. public void happy(double money, Consumer<Double> con) {
  33. con.accept(money);
  34. }
  35. //Supplier<T> 供给型接口 :
  36. @Test
  37. public void test2() {
  38. List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
  39. for (Integer num : numList) {
  40. System.out.println(num);
  41. }
  42. }
  43. //需求:产生指定个数的整数,并放入集合中
  44. public List<Integer> getNumList(int num, Supplier<Integer> sup) {
  45. List<Integer> list = new ArrayList<>();
  46. for (int i = 0; i < num; i++) {
  47. Integer n = sup.get();
  48. list.add(n);
  49. }
  50. return list;
  51. }
  52. //Function<T, R> 函数型接口:
  53. @Test
  54. public void test3() {
  55. String newStr = strHandler("\t\t\t 函数型接口 ", String::trim);
  56. System.out.println(newStr);
  57. String subStr = strHandler("函数型接口", (str) -> str.substring(2, 5));
  58. System.out.println(subStr);
  59. }
  60. //需求:用于处理字符串
  61. public String strHandler(String str, Function<String, String> fun) {
  62. return fun.apply(str);
  63. }
  64. //Predicate<T> 断言型接口:
  65. @Test
  66. public void test4() {
  67. List<String> list = Arrays.asList("Hello", "aa", "Lambda", "www", "ok");
  68. List<String> strList = filterStr(list, (s) -> s.length() > 3);
  69. for (String str : strList) {
  70. System.out.println(str);
  71. }
  72. }
  73. //需求:将满足条件的字符串,放入集合中
  74. public List<String> filterStr(List<String> list, Predicate<String> pre) {
  75. List<String> strList = new ArrayList<>();
  76. for (String str : list) {
  77. if (pre.test(str)) {
  78. strList.add(str);
  79. }
  80. }
  81. return strList;
  82. }
  83. }

常见的其他接口可以参考官方文档: https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html

参数引用

方法引用

若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用(可以将方法引用理解为 Lambda 表达式的另外一种表现形式)

  • 对象的引用 :: 实例方法名
  • 类名 :: 静态方法名
  • 类名 :: 实例方法名

__注意:

  1. 方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致
  2. 若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName

    构造器引用

    构造器的参数列表,需要与函数式接口中参数列表保持一致
  • 类名 :: new

    数组引用

  • 类型[] :: new

  1. package com.initit.java8.lambda表达式;
  2. import org.junit.Test;
  3. import java.io.PrintStream;
  4. import java.util.Comparator;
  5. import java.util.function.*;
  6. /*
  7. * 一、方法引用:若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用
  8. * (可以将方法引用理解为 Lambda 表达式的另外一种表现形式)
  9. *
  10. * 1. 对象的引用 :: 实例方法名
  11. *
  12. * 2. 类名 :: 静态方法名
  13. *
  14. * 3. 类名 :: 实例方法名
  15. *
  16. * 注意:
  17. * ①方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!
  18. * ②若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式: ClassName::MethodName
  19. *
  20. * 二、构造器引用 :构造器的参数列表,需要与函数式接口中参数列表保持一致!
  21. *
  22. * 1. 类名 :: new
  23. *
  24. * 三、数组引用
  25. *
  26. * 类型[] :: new;
  27. *
  28. *
  29. */
  30. public class 方法引用 {
  31. //数组引用
  32. @Test
  33. public void test8() {
  34. Function<Integer, String[]> fun = (args) -> new String[args];
  35. String[] strs = fun.apply(10);
  36. System.out.println(strs.length);
  37. System.out.println("--------------------------");
  38. Function<Integer, Employee[]> fun2 = Employee[]::new;
  39. Employee[] emps = fun2.apply(20);
  40. System.out.println(emps.length);
  41. }
  42. //构造器引用
  43. @Test
  44. public void test7() {
  45. Function<String, Employee> fun = Employee::new;
  46. BiFunction<String, Integer, Employee> fun2 = Employee::new;
  47. }
  48. @Test
  49. public void test6() {
  50. Supplier<Employee> sup = () -> new Employee();
  51. System.out.println(sup.get());
  52. System.out.println("------------------------------------");
  53. Supplier<Employee> sup2 = Employee::new;
  54. System.out.println(sup2.get());
  55. }
  56. //类名 :: 实例方法名
  57. @Test
  58. public void test5() {
  59. BiPredicate<String, String> bp = (x, y) -> x.equals(y);
  60. System.out.println(bp.test("abcde", "abcde"));
  61. System.out.println("-----------------------------------------");
  62. BiPredicate<String, String> bp2 = String::equals;
  63. System.out.println(bp2.test("abc", "abc"));
  64. System.out.println("-----------------------------------------");
  65. Function<Employee, String> fun = (e) -> e.show();
  66. System.out.println(fun.apply(new Employee()));
  67. System.out.println("-----------------------------------------");
  68. Function<Employee, String> fun2 = Employee::show;
  69. System.out.println(fun2.apply(new Employee()));
  70. }
  71. //类名 :: 静态方法名
  72. @Test
  73. public void test4() {
  74. Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
  75. System.out.println("-------------------------------------");
  76. Comparator<Integer> com2 = Integer::compare;
  77. }
  78. @Test
  79. public void test3() {
  80. BiFunction<Double, Double, Double> fun = (x, y) -> Math.max(x, y);
  81. System.out.println(fun.apply(1.5, 22.2));
  82. System.out.println("--------------------------------------------------");
  83. BiFunction<Double, Double, Double> fun2 = Math::max;
  84. System.out.println(fun2.apply(1.2, 1.5));
  85. }
  86. //对象的引用 :: 实例方法名
  87. @Test
  88. public void test2() {
  89. Employee emp = new Employee(101, "张三", 18, 9999.99);
  90. Supplier<String> sup = () -> emp.getName();
  91. System.out.println(sup.get());
  92. System.out.println("----------------------------------");
  93. Supplier<String> sup2 = emp::getName;
  94. System.out.println(sup2.get());
  95. }
  96. @Test
  97. public void test1() {
  98. PrintStream ps = System.out;
  99. Consumer<String> con = (str) -> ps.println(str);
  100. con.accept("Hello World!");
  101. System.out.println("--------------------------------");
  102. Consumer<String> con2 = ps::println;
  103. con2.accept("Hello Java8!");
  104. Consumer<String> con3 = System.out::println;
  105. }
  106. class Employee {
  107. private int id;
  108. private String name;
  109. private int age;
  110. private double salary;
  111. public Employee() {
  112. }
  113. public Employee(String name) {
  114. this.name = name;
  115. }
  116. public Employee(String name, int age) {
  117. this.name = name;
  118. this.age = age;
  119. }
  120. public Employee(int id, String name, int age, double salary) {
  121. this.id = id;
  122. this.name = name;
  123. this.age = age;
  124. this.salary = salary;
  125. }
  126. public int getId() {
  127. return id;
  128. }
  129. public void setId(int id) {
  130. this.id = id;
  131. }
  132. public String getName() {
  133. return name;
  134. }
  135. public void setName(String name) {
  136. this.name = name;
  137. }
  138. public int getAge() {
  139. return age;
  140. }
  141. public void setAge(int age) {
  142. this.age = age;
  143. }
  144. public double getSalary() {
  145. return salary;
  146. }
  147. public void setSalary(double salary) {
  148. this.salary = salary;
  149. }
  150. public String show() {
  151. return "测试方法引用!";
  152. }
  153. @Override
  154. public int hashCode() {
  155. final int prime = 31;
  156. int result = 1;
  157. result = prime * result + age;
  158. result = prime * result + id;
  159. result = prime * result + ((name == null) ? 0 : name.hashCode());
  160. long temp;
  161. temp = Double.doubleToLongBits(salary);
  162. result = prime * result + (int) (temp ^ (temp >>> 32));
  163. return result;
  164. }
  165. @Override
  166. public boolean equals(Object obj) {
  167. if (this == obj)
  168. return true;
  169. if (obj == null)
  170. return false;
  171. if (getClass() != obj.getClass())
  172. return false;
  173. Employee other = (Employee) obj;
  174. if (age != other.age)
  175. return false;
  176. if (id != other.id)
  177. return false;
  178. if (name == null) {
  179. if (other.name != null)
  180. return false;
  181. } else if (!name.equals(other.name))
  182. return false;
  183. if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
  184. return false;
  185. return true;
  186. }
  187. @Override
  188. public String toString() {
  189. return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
  190. }
  191. }
  192. }