Java8 新特性 Lambda表达式

简介

Java 8 引入的 Lambda 表达式是函数式编程在 Java 中的一个重要特性。Lambda 表达式允许你以更简洁的方式实现只有一个抽象方法的接口(即函数式接口)的匿名内部类。这使得 Java 代码更加简洁、易读和易维护。

函数式编程的概念

函数式编程(Functional Programming, FP)是一种编程范式,它强调将计算过程看作是函数之间的转换而不是状态的改变。函数式编程的核心思想是将计算机程序视为一系列数学函数的组合,通过函数来描述运算过程,并尽量不改变外界状态,从而避免多线程共享变量的问题。函数式编程的主要特性包括:

  1. 函数是一等公民:在函数式编程中,函数可以被当作参数传递给其他函数,也可以作为其他函数的返回值。
  2. 纯函数:纯函数是函数式编程中的核心概念,它指的是函数的输出仅由输入决定,不会对外部状态产生影响,也不会产生副作用。
  3. 不可变性:函数式编程通常使用不可变数据结构,这意味着数据一旦创建就不能被修改。
  4. 高阶函数:高阶函数是可以接受其他函数作为参数或返回一个函数的函数。
  5. 延迟计算:函数式编程支持延迟计算,即只有在需要的时候才进行计算,这可以避免不必要的计算。
  6. 递归(Recursion):使用递归而不是循环来实现重复的操作。
  7. 声明式编程(Declarative Programming):关注“做什么”而不是“怎么做”,与命令式编程(Imperative Programming)相对。
  8. 模式匹配(Pattern Matching):用于数据结构的解析和处理,如在 Haskell 或 Scala 中的模式匹配。
  9. 惰性评估(Lazy Evaluation):计算只有在需要时才执行,可以提高性能和内存效率。
  10. 并发编程(Concurrency):由于纯函数和不可变性的特性,函数式编程语言通常更容易处理并发和并行计算。

函数式编程的优点

函数式编程具有多个显著的优点,这些优点使得它在现代软件开发中越来越受到重视:

  1. 代码简洁性和可读性:函数式编程通过简洁的函数组合来表达复杂的逻辑,使得代码更加简洁易读。
  2. 模块化开发:函数式编程鼓励将程序拆分成多个小的函数,每个函数负责完成一个特定的任务,这有助于实现模块化开发,提高代码的可维护性。
  3. 可重用性:函数式编程中的函数可以被多次调用,提高了代码的可重用性,减少了代码重复。
  4. 易于测试和调试:由于函数式编程中的函数通常具有较小的依赖性和较少的副作用,因此更容易进行单元测试和调试。
  5. 并发安全性:由于函数式编程中的纯函数不会修改外部状态,因此可以更容易地实现并发编程,减少并发错误的发生。
  6. 支持数学和逻辑推导:函数式编程与数学和逻辑紧密相关,支持使用数学和逻辑推导来验证程序的正确性。

函数式接口

函数式接口是只有一个抽象方法的接口(可以有多个默认方法和静态方法)。@FunctionalInterface 注解是一个标记注解,用于表明接口是函数式接口,但这不是强制性的。如果接口不符合函数式接口的定义(即包含多个抽象方法),但使用了 @FunctionalInterface 注解,编译器会报错。

Lambda 表达式的优点和缺点

优点:

  • 代码简洁,开发迅速
  • 方便函数式编程
  • 容易进行并行计算
  • Java 引入 Lambda,改善了集合操作

缺点:

  • 代码可读性变差
  • 在非并行计算中,很多计算未必有传统 for 循环性能高
  • 不容易进行调试

Lambda 表达式与接口实现的比较

  1. 传统接口实现方式

    • 实现接口的类

      public interface MyFunctionalInterface {
          void doSomething();
      }
      
      public class MyFunctionalInterfaceImpl implements MyFunctionalInterface {
          @Override
          public void doSomething() {
              System.out.println("Doing something!");
          }
      }
      
    • 匿名内部类

      MyFunctionalInterface myFunc = new MyFunctionalInterface() {
          @Override
          public void doSomething() {
              System.out.println("Doing something!");
          }
      };
      
  2. Lambda 表达式

    Lambda 表达式的引入使得上述两种实现方式都变得更加简洁:

    MyFunctionalInterface myFunc = () -> System.out.println("Doing something!");
    
  3. 测试

@Test
void stream() {
    MyFunctionalInterfaceImpl myFunc = new MyFunctionalInterfaceImpl();
    myFunc.doSomething();
    MyFunctionalInterface myFunc1 = () -> System.out.println("Doing something2!");
    myFunc1.doSomething();
    MyFunctionalInterface myFunc2 = new MyFunctionalInterface() {
        @Override
        public void doSomething() {
            System.out.println("Doing something3!");
        }
    };
    myFunc2.doSomething();
}

1、Lambda 表达式的语法

1.1 Lambda 表达式提供了一种简洁的方式来表示匿名函数。其基本语法如下:

(参数列表) -> 表达式

 (parameters) -> expression

或 (参数列表) -> { Lambda 体 }

(parameters) -> { statements; }
  • 无参数

    Runnable runnable = () -> System.out.println("Hello, World!");
    
  • 一个参数

     Function<String, Integer> stringLength = s -> s.length();
    
  • 多个参数

     BinaryOperator<Integer> add = (a, b) -> a + b;
    
1.2 Java 7(使用匿名类)和 Java 8(使用 Lambda 表达式)之间参数形式的对比:
Java 7 (匿名类)Java 8 (Lambda 表达式)
new Comparator<String>() { @Override public int compare(String s1, String s2) { return s1.compareTo(s2); } }(s1, s2) -> s1.compareTo(s2)
new Runnable() { @Override public void run() { System.out.println("Hello, World!"); } }() -> System.out.println("Hello, World!")
new Callable<String>() { @Override public String call() throws Exception { return "Hello, Lambda!"; } }() -> "Hello, Lambda!"
new Function<Integer, Integer>() { @Override public Integer apply(Integer t) { return t * t; } }(t) -> t * t

2、Lambda表达式规则

  1. 参数类型可以省略:如果 Lambda 表达式的参数类型可以从上下文中推断出来,那么可以省略参数类型,例如 (int a)(a) 效果相同。

  2. 单参数的小括号可以省略:如果 Lambda 表达式只有一个参数,那么可以省略这个参数周围的小括号,例如 (a)a 效果相同。

  3. 单行方法体可以省略大括号:如果 Lambda 表达式的方法体只包含一条语句,那么可以省略大括号。

  4. 单行方法体的返回语句可以省略 return 关键字:如果 Lambda 表达式的方法体只有一条返回语句,那么可以省略 return 关键字。

3、示例

  1. 简单的Lambda表达式

    // 使用Lambda表达式打印信息
    Runnable r = () -> System.out.println("Hello, Lambda!");
    r.run();
    
  2. 使用单个参数的Lambda表达式

    // 使用Lambda表达式对集合中的每个元素执行操作
    List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
    names.forEach(name -> System.out.println(name));
    
  3. 使用多个参数的Lambda表达式

    // 使用Lambda表达式比较两个字符串
    List<String> strings = Arrays.asList("Apple", "Banana", "Cherry");
    Collections.sort(strings, (s1, s2) -> s2.compareTo(s1));
    strings.forEach(System.out::println);
    
  4. Lambda表达式作为方法参数

    // 使用Lambda表达式作为方法的参数
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
    int sum = numbers.stream().reduce(0, (acc, item) -> acc + item);
    System.out.println("Sum: " + sum);
    
  5. Lambda表达式与方法引用

    // 使用方法引用作为Lambda表达式
    numbers.forEach(System.out::println);
    
  6. Lambda表达式与构造器引用

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    class Person {
        private String name;
        private int age;
    }
    
    @Test
    void stream() {
        // 使用Lambda表达式创建对象
        Supplier<Person> personSupplierLambda = () -> new Person();
    
        // 使用构造器引用创建对象
        Supplier<Person> personSupplierRef = Person::new;
    
        // 使用Supplier来创建Person对象
        Person personSupplier1 = personSupplierLambda.get();
        Person personSupplier2 = personSupplierRef.get();
        personSupplier1.setAge(20);
        personSupplier1.setName("Alice");
        personSupplier2.setAge(18);
        personSupplier2.setName("Bob");
        // 如果Person有一个接受名字和年龄的构造器,我们可以这样使用它(假设存在一个合适的函数式接口)
        // 注意:这里需要自定义或找到一个合适的函数式接口,比如BiFunction<String, Integer, Person>
        // 但为了简单起见,我们仅展示概念
         BiFunction<String, Integer, Person> personCreator = Person::new;
         Person personSupplier3 = personCreator.apply("Charlie", 30);
        System.out.println(personSupplier1);
        System.out.println(personSupplier2);
        System.out.println(personSupplier3);
    }
    
  7. Lambda表达式与泛型

    // 使用Lambda表达式转换数据类型
    List<String> stringList = Arrays.asList("1", "2", "3");
    List<Integer> integerList = stringList.stream().map(Integer::parseInt).collect(Collectors.toList());
    integerList.forEach(System.out::println);
    
  8. Lambda表达式与高阶函数

    高阶函数的定义:

    • 接受函数作为参数:如果一个函数可以接受另一个函数作为参数,那么这个函数就是高阶函数。
    • 返回函数作为结果:如果一个函数可以返回另一个函数,那么这个函数也是高阶函数。
    // 使用Lambda表达式作为高阶函数的参数
    List<String> filtered = strings.stream()
        .filter(s -> s.startsWith("A"))
        .collect(Collectors.toList());
    
  9. Lambda表达式与排序

    // 使用Lambda表达式进行复杂排序
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    class Person {
        private String name;
        private int age;
    }
    
    @Test
    void stream() {
        List<Person> people = Arrays.asList(new Person("Alice", 30), new Person("Bob", 25));
        people.sort((p1, p2) -> p1.age - p2.age);
        System.out.println(people);
    }
    

这些示例展示了Lambda表达式在Java 8中的多种用法,包括简单的操作、集合处理、方法引用、泛型以及高阶函数。Lambda表达式使得代码更加简洁和易于理解。

  • 21
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值