基本用法

Lambda表达式本质:用作接口的实现(其实就是把之前实现接口的步骤简化了)。接口必须是函数式接口

一句话说明函数式接口:接口只有一个抽象方法 函数式接口可以使用@FunctionalInterface注解进行校验,非函数式接口使用此注解会报错

-> :Lambda操作符
->的左边:Lambda形参列表(即抽象方法中的形参列表)
->的右边:Lambda体(即重写后的抽象方法体)
格式:

  1.   <函数式接口> <变量名> = (参数1, 参数2...) -> {
  2.     //方法体
  3.   };
  4.  // 或者
  5.   <函数式接口> <变量名> = (参数1, 参数2...) -> 表达式;

参数个数可为0至n个。多个参数需要用逗号,——分割。 当参数个数为1时,括号可省略; 当参数个数为0时,括号不可省略; 参数前可以不加参数类型(不加会自动推导)。

Lambda 体,可以是一个表达式,也可以是语句块;如果是多条语句,需要使用大括号{}包裹;如果只有一条语句则可省略大括号{},但是必须同时省略return; 表达式中不能加入 return 语句,因为在表达式中已经隐含了 return 语句;但是语句块中没有隐含,需要使用 return 语句;

示例1:只有一个参数,一条语句

注:Consumer是java.util包下的一个函数式接口

基本写法(匿名实现函数式接口,并调用):

  1. Consumer<String> consumer = new Consumer<String>() {
  2. @Override
  3. public void accept(String s) {
  4. System.out.println(s);
  5. }
  6. };
  7. consumer.accept("测试方法一");

lambda写法(实现函数式接口,并调用):

  1. //完整写法:
  2. Consumer<String> consumer1 = (String s) -> {
  3. System.out.println(s);
  4. };
  5. consumer1.accept("测试方法一");
  6. //写法二(省略数据类型)
  7. Consumer<String> consumer2 = (s) -> {
  8. System.out.println(s);
  9. };
  10. consumer2.accept("测试方法三");
  11. //写法三(lambda只需要一个参数时,参数的小括号可省略)
  12. Consumer<String> consumer3 = s -> {
  13. System.out.println(s);
  14. };
  15. consumer3.accept("测试方法三");
  16. // 写法四(最简写法)
  17. Consumer<String> consumer4 = s -> System.out.println(s);
  18. consumer4.accept("测试方法四");

示例2:只有一条语句,没有参数

  1. Runnable r1 = new Runnable() {
  2. @Override
  3. public void run() {
  4. System.out.println("123");
  5. }
  6. };

lambda:

  1. Runnable r2 = () -> System.out.println("abc");

示例3:只有一条语句,且为返回语句
普通写法:

  1. Comparator<Integer> comparator21 = new Comparator<Integer>() {
  2. @Override
  3. public int compare(Integer o1, Integer o2) {
  4. return Integer.compare(o1,o2);
  5. }
  6. };

lambda:

  1. //只有一条语句时,可以省略大括号,如果是return的话,可直接省略return
  2. Comparator<Integer> comparator22 = (o1, o2) -> Integer.compare(o1,o2);

示例四:参数个数为多条,语句为多条,且有返回值
基本写法:

  1. Comparator<Integer> comparator1 = new Comparator<Integer>() {
  2. @Override
  3. public int compare(Integer o1, Integer o2) {
  4. System.out.println("一");
  5. System.out.println("二");
  6. return Integer.compare(o1,o2);
  7. }
  8. };
  9. System.out.println(comparator1.compare(0,9));

lambda:

  1. Comparator<Integer> comparator2 = (o1, o2) -> {
  2. System.out.println("一");
  3. System.out.println("二");
  4. return Integer.compare(o1,o2);
  5. };
  6. System.out.println(comparator2.compare(0,9));

函数式接口

函数式接口只有一个抽象方法,但是可以有多个非抽象方法的接口(同理,任何接口,有且只有一个抽象方法,那么它就是一个函数式接口)。 我们在自定义函数式接口时,可以使用@FunctionalInterface注解进行约束校验。

自定义函数式接口
示例1:

  1. @FunctionalInterface
  2. interface FunctionTest{
  3. void testMethod(String name, int age);
  4. default void method2() {
  5. // 函数式接口只能有一个抽象方法,但是可以有多个非抽象方法
  6. }
  7. }

lambda:

  1. @Test
  2. public void LambdaTest(){
  3. FunctionTest functionTest = (name, age) -> System.out.println("姓名为:" + name + " | 年龄为:" + age);
  4. functionTest.testMethod("ahzoo",18); // 姓名为:ahzoo | 年龄为:18
  5. }

示例2:

  1. import lombok.AllArgsConstructor;
  2. import lombok.Data;
  3. import lombok.NoArgsConstructor;
  4. import org.junit.jupiter.api.Test;
  5. public class MethodTest {
  6. @AllArgsConstructor
  7. @NoArgsConstructor
  8. @Data
  9. class User{
  10. private String name;
  11. }
  12. @FunctionalInterface
  13. interface UserService{
  14. User getUser();
  15. }
  16. @FunctionalInterface
  17. interface UserService2{
  18. User getUser(String name);
  19. }
  20. @Test
  21. public void userTest(){
  22. // 调用无参接口
  23. UserService userService = User::new;
  24. User user = userService.getUser();
  25. System.out.println(user); // MethodTest.User(name=null)
  26. // 调用有参接口
  27. UserService2 userService2 = User::new;
  28. User user2 = userService2.getUser("ahzoo");
  29. System.out.println(user2); // MethodTest.User(name=ahzoo)
  30. System.out.println(user2.getName()); // ahzoo
  31. }
  32. }

引用方法

使用时机:

当我们不想重写某个匿名内部类的方法时,就可以使用lambda表达式的接口快速指向一个已经被实现的方法。将一个方法赋给一个变量或者作为参数传递给另外一个方法。

前提:

参数数量和类型与接口中定义的一致; 返回值类型与接口中定义的一致(一般也是一个函数式接口类型)。

引用格式:
方法归属者::方法名

静态方法的归属者为类名,普通方法归属者为对象; 方法名后面不能带参数。

常见引用:
对象::实例方法(例:instanceName::methodName
类::静态方法(例:className::methodName
类::实例方法

this::实例方法
super::实例方法

构造器::new
数组::new

示例一:

  1. import java.util.Arrays;
  2. import java.util.List;
  3. public class MethodTest {
  4. public static void main(String[] args) {
  5. List<String> list = Arrays.asList("777", "999");
  6. // 1. 对象::实例方法
  7. // 不使用引用:
  8. list.forEach(s -> new MethodTest().print(s));
  9. // 使用引用
  10. list.forEach(new MethodTest()::print);
  11. // 2. 类::实例方法
  12. // 不使用引用:
  13. list.forEach(s -> MethodTest.toTest(s));
  14. // 使用引用
  15. list.forEach(MethodTest::toTest);
  16. // 3. 类::静态方法
  17. // 不使用引用
  18. list.forEach(s -> System.out.println(s));
  19. // 使用引用
  20. list.forEach(System.out::println);
  21. }
  22. public void print(String s) {
  23. System.out.println("打印:" + s);
  24. }
  25. public static void toTest(String s) {
  26. System.out.println("静态:"+s);
  27. }
  28. }

示例2:

  1. import java.util.function.Supplier;
  2. import org.junit.jupiter.api.Test;
  3. public class MethodTest {
  4. @Test
  5. public void test() {
  6. // 不使用引用
  7. Supplier<String> supplier = ()->new String();
  8. System.out.println(supplier);
  9. // 使用引用
  10. Supplier<String> supplier2 = String::new;
  11. System.out.println(supplier2);
  12. }
  13. }

作用域

在匿名内部类中,如果要引用外部变量,需要先将变量声明为final; 虽然在lambda表达式中并未要求一定要将外部变量声明为final,但其实在表达式中变量已经被隐式的声明为final,是不能对其进行修改的; 在lambda表达式中无法声明和局部变量相同的变量

错误示例:

  1. int i = 1;
  2. // 在lambda表达式中无法对外部变量(i)进行修改
  3. Consumer<String> consumer = s -> System.out.println(i++); // 报错

错误示例:

  1. // 在lambda表达式中无法声明和局部变量(consumer)相同的变量(consumer)
  2. Consumer<String> consumer = consumer -> System.out.println(consumer); // 报错

常用场景

其实在前面的示例中都已经写过了,这里进行一个总结

遍历

  1. List<String> list = Arrays.asList("777", "999");
  2. // 增强for
  3. for(String i : list){
  4. System.out.println(i);
  5. }
  6. // lambda写法:方法引用
  7. list.forEach(System.out::println);

排序

  1. List<Integer> list = Arrays.asList(999, 333, 777, 111);
  2. // 匿名实现写法: 正序排序
  3. list.sort(new Comparator<Integer>() {
  4. @Override
  5. public int compare(Integer o1, Integer o2) {
  6. return Integer.compare(o1,o2);
  7. }
  8. });
  9. // lambda写法
  10. // 正序排序时,参数数量和类型与接口中定义的一致(都是(m,n)),所以可以直接使用方法的引用
  11. list.sort(Integer::compare);
  12. System.out.println(list); // [111, 333, 777, 999]
  13. // 倒序排序时,参数为(m,n),接口中为(n,m),不一致,所以不能使用方法是引用
  14. list.sort((m, n) -> Integer.compare(n, m));
  15. System.out.println(list); // [999, 777, 333, 111]

线程

  1. // 匿名实现写法
  2. Runnable runnable = new Runnable() {
  3. @Override
  4. public void run() {
  5. System.out.println("启动新线程");
  6. }
  7. };
  8. new Thread(runnable).start();
  9. // lambda写法
  10. new Thread(() -> System.out.println("启动新线程")).start();