1. 什么是Lambda表达式?

  • Lambda 表达式,是一个匿名函数,也可称为闭包。
  • 我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)
  • Lambda 允许把函数作为一个方法的参数
  • Lambda表达式的本质:作为函数式接口的实例,匿名实现类的对象
  • 使用 Lambda 表达式可以使代码变的更加简洁紧凑。
  • 学习Lambda要打好基础,接口的多态引用很重要。

2. lambda 表达式的语法格式

lambda 表达式的语法格式如下:

  1. (parameters) -> expression
  2. (parameters) ->{ statements; }

以下是lambda表达式的重要特征:

  • 可选类型声明:Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序 的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于 上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。image.png
  • 可选的参数圆括号():一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号{}:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的return返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值
  • 左边:lambda形参列表 (其实就是接口中的抽象方法的形参列表)
  • 右边:lambda体 (其实就是重写的抽象方法的方法体)

Lambda 表达式的简单例子:

  1. // 1. 不需要参数,返回值为 5
  2. () -> 5
  3. // 2. 接收一个参数(数字类型),返回其2倍的值
  4. x -> 2 * x
  5. // 3. 接受2个参数(数字),并返回他们的差值
  6. (x, y) -> x y
  7. // 4. 接收2个int型整数,返回他们的和
  8. (int x, int y) -> x + y
  9. // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
  10. (String s) -> System.out.print(s)
  1. public class Java8Tester {
  2. public static void main(String args[]){
  3. Java8Tester tester = new Java8Tester();
  4. // 类型声明
  5. MathOperation addition = (int a, int b) -> a + b;
  6. // 不用类型声明
  7. MathOperation subtraction = (a, b) -> a - b;
  8. // 大括号中的返回语句
  9. MathOperation multiplication = (int a, int b) -> { return a * b; };
  10. // 没有大括号及返回语句
  11. MathOperation division = (int a, int b) -> a / b;
  12. System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
  13. System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
  14. System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
  15. System.out.println("10 / 5 = " + tester.operate(10, 5, division));
  16. // 不用括号
  17. GreetingService greetService1 = message -> System.out.println("Hello " + message);
  18. // 用括号
  19. GreetingService greetService2 = (message) -> System.out.println("Hello " + message);
  20. greetService1.sayMessage("World");
  21. greetService2.sayMessage("China");
  22. }
  23. interface MathOperation {
  24. int operation(int a, int b);
  25. }
  26. interface GreetingService {
  27. void sayMessage(String message);
  28. }
  29. private int operate(int a, int b, MathOperation mathOperation){
  30. return mathOperation.operation(a, b);
  31. }
  32. }

image.png

另外一些案例:

  1. import org.junit.Test;
  2. import java.util.ArrayList;
  3. import java.util.Comparator;
  4. import java.util.function.Consumer;
  5. public class LambdaTest1 {
  6. //语法格式一:无参,无返回值
  7. @Test
  8. public void test1(){
  9. Runnable r1 = new Runnable() {
  10. @Override
  11. public void run() {
  12. System.out.println("我爱北京天安门");
  13. }
  14. };
  15. r1.run();
  16. System.out.println("***********************");
  17. Runnable r2 = () -> {
  18. System.out.println("我爱北京故宫");
  19. };
  20. r2.run();
  21. System.out.println("-----------test1 end-----------------");
  22. }
  23. //语法格式二:Lambda 需要一个参数,但是没有返回值。
  24. @Test
  25. public void test2(){
  26. Consumer<String> con = new Consumer<String>() {
  27. @Override
  28. public void accept(String s) {
  29. System.out.println(s);
  30. }
  31. };
  32. con.accept("谎言和誓言的区别是什么?");
  33. System.out.println("*******************");
  34. Consumer<String> con1 = (String s) -> {
  35. System.out.println(s);
  36. };
  37. con1.accept("一个是听得人当真了,一个是说的人当真了");
  38. System.out.println("-----------test2 end-----------------");
  39. }
  40. //语法格式三:数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
  41. @Test
  42. public void test3(){
  43. Consumer<String> con1 = (String s) -> {
  44. System.out.println(s);
  45. };
  46. con1.accept("一个是听得人当真了,一个是说的人当真了");
  47. System.out.println("*******************");
  48. Consumer<String> con2 = (s) -> {
  49. System.out.println(s);
  50. };
  51. con2.accept("一个是听得人当真了,一个是说的人当真了");
  52. System.out.println("-----------test3 end-----------------");
  53. }
  54. //语法格式四:Lambda 若只需要一个参数时,参数的小括号可以省略
  55. @Test
  56. public void test5(){
  57. Consumer<String> con1 = (s) -> {
  58. System.out.println(s);
  59. };
  60. con1.accept("一个是听得人当真了,一个是说的人当真了");
  61. System.out.println("*******************");
  62. Consumer<String> con2 = s -> {
  63. System.out.println(s);
  64. };
  65. con2.accept("一个是听得人当真了,一个是说的人当真了");
  66. System.out.println("-----------test5 end-----------------");
  67. }
  68. //语法格式五:Lambda 需要两个或以上的参数,多条执行语句,并且可以有返回值
  69. @Test
  70. public void test6(){
  71. Comparator<Integer> com1 = new Comparator<Integer>() {
  72. @Override
  73. public int compare(Integer o1, Integer o2) {
  74. System.out.println(o1);
  75. System.out.println(o2);
  76. return o1.compareTo(o2);
  77. }
  78. };
  79. System.out.println(com1.compare(12,21));
  80. System.out.println("*****************************");
  81. Comparator<Integer> com2 = (o1,o2) -> {
  82. System.out.println(o1);
  83. System.out.println(o2);
  84. return o1.compareTo(o2);
  85. };
  86. System.out.println(com2.compare(12,6));
  87. System.out.println("-----------test6 end-----------------");
  88. }
  89. //语法格式六:当 Lambda 体只有一条语句时,return 与大括号若有,都可以省略
  90. @Test
  91. public void test7(){
  92. Comparator<Integer> com1 = (o1,o2) -> {
  93. return o1.compareTo(o2);
  94. };
  95. System.out.println(com1.compare(12,6));
  96. System.out.println("*****************************");
  97. Comparator<Integer> com2 = (o1,o2) -> o1.compareTo(o2);
  98. System.out.println(com2.compare(2,21));
  99. System.out.println("-----------test7 end-----------------");
  100. }
  101. @Test
  102. public void test8(){
  103. Consumer<String> con1 = s -> {
  104. System.out.println(s);
  105. };
  106. con1.accept("一个是听得人当真了,一个是说的人当真了");
  107. System.out.println("*****************************");
  108. Consumer<String> con2 = s -> System.out.println(s);
  109. con2.accept("一个是听得人当真了,一个是说的人当真了");
  110. System.out.println("-----------test8 end-----------------");
  111. }
  112. }

image.png

3. 函数式(Functional)接口

  • 只包含一个抽象方法的接口,称为函数式接口。
  • 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式 抛出一个受检异常(即:非运行时异常),那么该异常需要在目标接口的抽象方法上进行声明)。
  • 我们可以在一个接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口。同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
  • 在java.util.function包下定义了Java 8 的丰富的函数式接口
  • Java从诞生日起就是一直倡导“一切皆对象”,在Java里面面向对象(OOP) 编程是一切。但是随着python、scala等语言的兴起和新技术的挑战,Java不得不做出调整以便支持更加广泛的技术要求,也即java不但可以支持OOP还 可以支持OOF(面向函数编程)
  • 在函数式编程语言当中,函数被当做一等公民对待。在将函数作为一等公民的 编程语言中,Lambda表达式的类型是函数。但是在Java8中,有所不同。在 Java8中,Lambda表达式是对象,而不是函数,它们必须依附于一类特别的 对象类型——函数式接口。
  • 简单的说,在Java8中,Lambda表达式就是一个函数式接口的实例,匿名实现类的对象。这就是 Lambda表达式和函数式接口的关系。也就是说,只要一个对象是函数式接口的实例,那么该对象就可以用Lambda表达式来表示。所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。

image.png
image.png

4. Java 内置四大核心函数式接口

image.png
image.png
使用消费型接口 Consumer void accept(T t)案例:

  1. @Test
  2. public void test1(){
  3. happyTime(500, new Consumer<Double>() {
  4. @Override
  5. public void accept(Double aDouble) {
  6. System.out.println("学习太累了,去买了瓶矿泉水,价格为:" + aDouble);
  7. }
  8. });
  9. System.out.println("********************");
  10. //Lambda重写
  11. happyTime(400,money -> System.out.println("学习太累了,去买了瓶矿泉水,价格为:" + money));
  12. }
  13. public void happyTime(double money, Consumer<Double> con){
  14. con.accept(money);
  15. }

image.png
使用Lambda表达式,过滤数据案例:

  1. @Test
  2. public void test2(){
  3. List<String> list = Arrays.asList("北京","杭州","南京","湖北","湖南","天津","东京","西京","普京");
  4. List<String> filterStrs = filterString(list, new Predicate<String>() {
  5. @Override
  6. public boolean test(String s) {
  7. return s.contains("京");
  8. }
  9. });
  10. System.out.println(filterStrs);
  11. //Lambda重写
  12. List<String> filterStrs1 = filterString(list,s -> s.contains("京"));
  13. System.out.println(filterStrs1);
  14. }
  15. //根据给定的规则,过滤集合中的字符串。此规则由Predicate的方法决定
  16. public List<String> filterString(List<String> list, Predicate<String> pre){
  17. ArrayList<String> filterList = new ArrayList<>();
  18. for(String s : list){
  19. if(pre.test(s)){
  20. filterList.add(s);
  21. }
  22. }
  23. return filterList;
  24. }

image.png

5. 方法引用(Method References)

  • 当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用! 减少代码,代码复用!!!
  • 方法引用可以看做是Lambda表达式深层次的表达。换句话说,方法引用就 是Lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向 一个方法,可以认为是Lambda表达式的一个语法糖。
  • 要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的 方法的参数列表和返回值类型保持一致!
  • 格式:使用操作符 “::” 将类(或对象) 与 方法名分隔开来。
  • 如下三种主要使用情况:
    • 对象::实例方法名
    • 类::静态方法名
    • 类::实例方法名

image.png
image.png
使用核心函数接口实现方法引用:

  1. import org.junit.Test;
  2. import java.io.PrintStream;
  3. import java.util.Comparator;
  4. import java.util.function.BiPredicate;
  5. import java.util.function.Consumer;
  6. import java.util.function.Function;
  7. import java.util.function.Supplier;
  8. public class MethodRefTest {
  9. // 情况一:对象 :: 实例方法
  10. //Consumer中的void accept(T t)
  11. //PrintStream中的void println(T t)
  12. @Test
  13. public void test1() {
  14. //lambda原生实现
  15. Consumer<String> con1 = str -> System.out.println(str);
  16. con1.accept("北京");
  17. System.out.println("****");
  18. //方法引用实现
  19. PrintStream ps = System.out;
  20. Consumer<String> con2 = ps::println;
  21. con2.accept("beijing");
  22. System.out.println("----------- test1() -------------");
  23. }
  24. //Supplier中的T get()
  25. //Employee中的String getName()
  26. @Test
  27. public void test2() {
  28. Employee emp = new Employee(1001,"Tom",23,5600);
  29. Supplier<String> sup1 = () -> emp.getName();
  30. System.out.println(sup1.get());
  31. System.out.println("****");
  32. Supplier<String> sup2 = emp::getName;
  33. System.out.println(sup2.get());
  34. System.out.println("----------- test2() -------------");
  35. }
  36. // 情况二:类 :: 静态方法
  37. //Comparator中的int compare(T t1,T t2)
  38. //Integer中的int compare(T t1,T t2)
  39. @Test
  40. public void test3() {
  41. Comparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
  42. System.out.println(com1.compare(12,21));
  43. System.out.println("****");
  44. Comparator<Integer> com2 = Integer::compare;
  45. System.out.println(com2.compare(12,3));
  46. System.out.println("----------- test3() -------------");
  47. }
  48. //Function中的R apply(T t)
  49. //Math中的Long round(Double d)
  50. @Test
  51. public void test4() {
  52. Function<Double,Long> func = new Function<Double, Long>() {
  53. @Override
  54. public Long apply(Double d) {
  55. return Math.round(d);
  56. }
  57. };
  58. System.out.println(func.apply(12.7));
  59. System.out.println("****");
  60. Function<Double,Long> func1 = d -> Math.round(d);
  61. System.out.println(func1.apply(12.3));
  62. System.out.println("****");
  63. Function<Double,Long> func2 = Math::round;
  64. System.out.println(func2.apply(12.6));
  65. System.out.println("----------- test4() -------------");
  66. }
  67. // 情况三:类 :: 实例方法 (有难度)
  68. // Comparator中的int comapre(T t1,T t2)
  69. // String中的int t1.compareTo(t2)
  70. @Test
  71. public void test5() {
  72. Comparator<String> com1 = (s1,s2) -> s1.compareTo(s2);
  73. System.out.println(com1.compare("abc","abd"));
  74. System.out.println("****");
  75. Comparator<String> com2 = String :: compareTo;
  76. System.out.println(com2.compare("abd","abf"));
  77. System.out.println("----------- test5() -------------");
  78. }
  79. //BiPredicate中的boolean test(T t1, T t2);
  80. //String中的boolean t1.equals(t2)
  81. @Test
  82. public void test6() {
  83. BiPredicate<String,String> pre1 = (s1,s2) -> s1.equals(s2);
  84. System.out.println(pre1.test("abc","abc"));
  85. System.out.println("****");
  86. BiPredicate<String,String> pre2 = String :: equals;
  87. System.out.println(pre2.test("abc","abd"));
  88. System.out.println("----------- test6() -------------");
  89. }
  90. // Function中的R apply(T t)
  91. // Employee中的String getName();
  92. @Test
  93. public void test7() {
  94. Employee employee = new Employee(1001, "Jerry", 23, 6000);
  95. Function<Employee,String> func1 = e -> e.getName();
  96. System.out.println(func1.apply(employee));
  97. System.out.println("****");
  98. Function<Employee,String> func2 = Employee::getName;
  99. System.out.println(func2.apply(employee));
  100. System.out.println("----------- test7() -------------");
  101. }
  102. }

image.png

6. 构造器引用

  • 格式: ClassName::new 与函数式接口相结合,自动与函数式接口中方法兼容。
  • 可以把构造器引用赋值给定义的方法,要求构造器参数列表要与接口中抽象 方法的参数列表一致!且方法的返回值即为构造器对应类的对象。

image.png

  1. package Random_name.sgm.day12.src.com.atguigu.java2;
  2. import org.junit.Test;
  3. import java.util.Arrays;
  4. import java.util.function.BiFunction;
  5. import java.util.function.Function;
  6. import java.util.function.Supplier;
  7. /**
  8. * 一、构造器引用
  9. * 和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致。
  10. * 抽象方法的返回值类型即为构造器所属的类的类型
  11. *
  12. * 二、数组引用
  13. * 大家可以把数组看做是一个特殊的类,则写法与构造器引用一致。
  14. */
  15. public class ConstructorRefTest {
  16. //构造器引用
  17. //Supplier中的T get()
  18. //Employee的空参构造器:Employee()
  19. @Test
  20. public void test1(){
  21. Supplier<Employee> sup = new Supplier<Employee>() {
  22. @Override
  23. public Employee get() {
  24. return new Employee();
  25. }
  26. };
  27. System.out.println("****");
  28. Supplier<Employee> sup1 = () -> new Employee();
  29. System.out.println(sup1.get());
  30. System.out.println("****");
  31. Supplier<Employee> sup2 = Employee :: new;
  32. System.out.println(sup2.get());
  33. System.out.println("------------ test1() ----------");
  34. }
  35. //Function中的R apply(T t)
  36. @Test
  37. public void test2(){
  38. Function<Integer,Employee> func1 = id -> new Employee(id);
  39. Employee employee = func1.apply(1001);
  40. System.out.println(employee);
  41. System.out.println("****");
  42. Function<Integer,Employee> func2 = Employee :: new;
  43. Employee employee1 = func2.apply(1002);
  44. System.out.println(employee1);
  45. System.out.println("------------ test2() ----------");
  46. }
  47. //BiFunction中的R apply(T t,U u)
  48. @Test
  49. public void test3(){
  50. BiFunction<Integer,String,Employee> func1 = (id,name) -> new Employee(id,name);
  51. System.out.println(func1.apply(1001,"Tom"));
  52. System.out.println("****");
  53. BiFunction<Integer,String,Employee> func2 = Employee :: new;
  54. System.out.println(func2.apply(1002,"Tom"));
  55. System.out.println("------------ test3() ----------");
  56. }
  57. }

image.png

7. 数组引用

  • 格式: type[] :: new

image.png

  1. //数组引用
  2. //Function中的R apply(T t)
  3. @Test
  4. public void test4(){
  5. Function<Integer,String[]> func1 = length -> new String[length];
  6. String[] arr1 = func1.apply(5);
  7. System.out.println(Arrays.toString(arr1));
  8. System.out.println("****");
  9. Function<Integer,String[]> func2 = String[] :: new;
  10. String[] arr2 = func2.apply(10);
  11. System.out.println(Arrays.toString(arr2));
  12. System.out.println("------------ test4() ----------");
  13. }

image.png