一,Lambda表达式

Java Lambda表达式与枚举详解:深入理解与实践

Java 8引入了Lambda表达式和改进的枚举类型,这些特性极大地丰富了Java语言的表达能力,使得代码更加简洁和富有表现力。本文将深入探讨Lambda表达式和枚举的概念、用法,并提供详细的代码示例。

Lambda表达式

概述

Lambda表达式是Java 8中引入的一种新语法,用于简化匿名内部类的创建,使得函数式编程在Java中成为可能。Lambda表达式允许我们以更紧凑的形式传递代码块,而不是传统的匿名内部类。

使用场景

Lambda表达式适用于以下场景:

  1. 接口中只有一个抽象方法
  2. 编译器能够通过上下文信息推断出参数类型

基本语法

Lambda表达式由箭头操作符->分为两部分:

  • 左侧:参数列表(可能为空),参数类型可以省略。
  • 右侧:Lambda体,即要执行的代码块。

分类

Lambda表达式是Java 8中引入的一个特性,它允许你以简洁的语法编写匿名函数。以下是你提到的Lambda表达式的分类,以及每种类型的具体要求和示例代码:

  1. 无参数,无返回值

    • 要求:Lambda表达式不接受任何参数,也没有返回值。
    • 示例代码:
      () -> System.out.println("Hello, World!");
      
  2. 一个参数,无返回值

    • 要求:Lambda表达式接受一个参数,但没有返回值。
    • 示例代码:
      (String str) -> System.out.println(str);
      
  3. 一个参数,省略小括号

    • 要求:当Lambda表达式只有一个参数时,可以省略小括号。
    • 示例代码:
      str -> System.out.println(str);
      
  4. 两个以上参数,有返回值

    • 要求:Lambda表达式接受两个或更多参数,并返回一个值。
    • 示例代码:
      (int a, int b) -> a + b;
      
  5. 一条语句,省略return和大括号

    • 要求:如果Lambda表达式体只有一条语句,则可以省略大括号和return关键字。
    • 示例代码:
      (String str1, String str2) -> str1.concat(str2);
      
  6. 类型推断

    • 要求:如果Lambda表达式的参数类型可以由上下文推断出来,那么可以省略参数类型。
    • 示例代码:
      List<String> list = Arrays.asList("a", "b", "c");
      list.forEach(str -> System.out.println(str));
      

在这个例子中,str的类型可以被推断为String,因为forEach方法接受一个Consumer<String>类型的Lambda表达式。

请注意,Lambda表达式通常与函数式接口一起使用,函数式接口是一个只有一个抽象方法的接口。例如,RunnableCallableComparator等都是函数式接口。Lambda表达式可以作为这些接口的实现,提供抽象方法的实现体。

Java内置接口

Java 8 引入了函数式编程的概念,其中最核心的部分就是函数式接口。函数式接口只有一个抽象方法,可以使用 @FunctionalInterface 注解来声明。以下是对您提供的内置函数式接口的详细补充,包括代码实例和详解。

1. Predicate

Predicate<T> 接口是接受一个输入参数,返回一个布尔值的函数式接口。它通常用于测试某个条件。

代码实例:

import java.util.function.Predicate;

public class PredicateExample {
    public static void main(String[] args) {
        Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
        
        // 使用 test 方法测试字符串是否非空
        System.out.println(nonEmptyStringPredicate.test("Hello")); // 输出 true
        System.out.println(nonEmptyStringPredicate.test("")); // 输出 false
    }
}

详解:

  • Predicate<String> 表示接受一个 String 类型的参数。
  • nonEmptyStringPredicate 是一个实现了 Predicate 接口的 lambda 表达式,它检查字符串是否非空。
  • test 方法用来执行 lambda 表达式中的逻辑。

2. Function<T, R>

Function<T, R> 接口接受一个输入参数,并返回一个结果。它通常用于对输入进行某种形式的转换。

代码实例:

import java.util.function.Function;

public class FunctionExample {
    public static void main(String[] args) {
        Function<String, Integer> lengthFunction = String::length;
        
        // 使用 apply 方法获取字符串长度
        System.out.println(lengthFunction.apply("Hello")); // 输出 5
    }
}

详解:

  • Function<String, Integer> 表示接受一个 String 类型的参数,并返回一个 Integer 类型的结果。
  • lengthFunction 是一个 lambda 表达式,它使用 String 类的 length 方法来获取字符串的长度。
  • apply 方法用来执行 lambda 表达式中的逻辑。

3. Supplier

Supplier<T> 接口不接受任何参数,但返回一个结果。它通常用于获取或生成一个值。

代码实例:

import java.util.function.Supplier;

public class SupplierExample {
    public static void main(String[] args) {
        Supplier<String> usernameSupplier = () -> "Kimi";
        
        // 使用 get 方法获取用户名
        System.out.println(usernameSupplier.get()); // 输出 Kimi
    }
}

详解:

  • Supplier<String> 表示返回一个 String 类型的结果。
  • usernameSupplier 是一个 lambda 表达式,它返回一个固定的字符串 "Kimi"。
  • get 方法用来执行 lambda 表达式并获取结果。

4. Consumer

Consumer<T> 接口接受一个输入参数,但不返回任何结果。它通常用于执行一个操作,如打印、记录日志等。

代码实例:

import java.util.function.Consumer;

public class ConsumerExample {
    public static void main(String[] args) {
        Consumer<String> printConsumer = System.out::println;
        
        // 使用 accept 方法打印字符串
        printConsumer.accept("Hello, World!"); // 输出 Hello, World!
    }
}

详解:

  • Consumer<String> 表示接受一个 String 类型的参数。
  • printConsumer 是一个 lambda 表达式,它使用 System.out::println 方法来打印字符串。
  • accept 方法用来执行 lambda 表达式中的逻辑。

这些函数式接口提供了一种简洁的方式来表示和使用函数,它们在 Java 8 及以后的版本中被广泛用于流操作、并发编程等领域。

方法引用

方法引用是 Java 8 引入的一个特性,它允许你将方法直接引用为一个函数式接口的实例,而不需要编写完整的 lambda 表达式。这在 lambda 表达式体中只是简单调用一个方法时非常有用。以下是对方法引用的详细补充,包括代码实例和详解。

1. 实例方法引用

实例方法引用使用语法 对象::实例方法名

代码实例:

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class InstanceMethodReference {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("a", "b", "c");
        
        // 使用方法引用来获取字符串的长度
        Function<String, Integer> lengthFunction = String::length;
        
        // 使用方法引用来转换列表中的字符串为它们的长度
        List<Integer> lengths = list.stream().map(lengthFunction).collect(Collectors.toList());
        
        System.out.println(lengths); // 输出 [1, 1, 1]
    }
}

详解:

  • String::length 是一个方法引用,它引用了 String 类的 length 实例方法。
  • map 方法接受一个函数式接口,这里我们使用方法引用来代替 lambda 表达式。

2.静态方法引用

静态方法引用使用语法 类名::静态方法名
代码实例:

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

public class StaticMethodReference {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        
        // 使用方法引用来获取数字的绝对值
        Function<Integer, Integer> absFunction = Math::abs;
        
        // 使用方法引用来转换列表中的数字为它们的绝对值
        List<Integer> absoluteNumbers = numbers.stream().map(absFunction).collect(Collectors.toList());
        
        System.out.println(absoluteNumbers); // 输出 [1, 2, 3, 4, 5]
    }
}

详解:

  • Math::abs 是一个方法引用,它引用了 Math 类的 abs 静态方法。
  • 由于 abs 方法是静态的,我们不需要一个 Math 类的实例就可以调用它。

3.构造方法引用

构造方法引用使用语法 类名::new

代码实例:

import java.util.function.Supplier;

public class ConstructorReference {
    public static void main(String[] args) {
        // 使用构造方法引用来创建新的String实例
        Supplier<String> stringSupplier = String::new;
        
        String str = stringSupplier.get("Hello, World!");
        
        System.out.println(str); // 输出 Hello, World!
    }
}

详解:

  • String::new 是一个构造方法引用,它引用了 String 类的构造方法。
  • get 方法接受一个字符串数组,这里我们使用方法引用来创建一个新的 String 实例。

4. 数组引用

数组引用使用语法类型[]::new

代码实例:

import java.util.function.Supplier;

public class ArrayReference {
    public static void main(String[] args) {
        // 使用数组引用来创建新的int数组
        Supplier<int[]> intArraySupplier = int[]::new;
        
        int[] intArray = intArraySupplier.get(5); // 创建一个长度为5的int数组
        
        System.out.println(intArray.length); // 输出 5
    }
}

详解:

  • int[]::new 是一个数组引用,它引用了 int 类型的数组构造方法。
  • get 方法接受一个整数参数,表示数组的长度。

方法引用提供了一种更简洁的方式来使用函数式接口,特别是在 lambda 表达式体中只是简单调用一个方法时。这使得代码更加简洁和可读。

枚举类型

概述

枚举是Java中用于定义固定常量集的类型。枚举类型将常量封装为对象,提供了丰富的方法和更好的类型安全检查。

代码示例


/*
    1. 创建枚举类的属性(成员遍历),必须是作为私有常量出现
    2. 必须将构造方法私有化,这是为了保证类的对象是有限个的目的
    3. 提供公共的静态的final方法给外界获取枚举类中多个对象
    4. 提供公共的获取属性的方法
    5. 重写toString()方法

 */
class Season{
    //1. 创建枚举类的属性(成员遍历),必须是作为私有常量出现
    private String name;
    private String info;

    //2. 必须将构造方法私有化,这是为了保证类的对象是有限个的目的
    private Season(String name, String info){
        this.name = name;
        this.info = info;
    }

    //3. 提供公共的静态的final方法给外界获取枚举类中多个对象
    public static final Season SPRING = new Season("春天","春暖花开");
    public static final Season SUMMER = new Season("夏天","烈日炎炎");
    public static final Season AUTUMN = new Season("秋天","秋高气爽");
    public static final Season WINTER = new Season("冬天","白雪皑皑");

JDK 1.5之前与之后的枚举

在JDK 1.5之前,枚举需要手动定义属性和方法。JDK 1.5及之后,枚举的定义更加简洁,可以直接列出枚举值,并且可以包含属性和方法。

实现接口

枚举类型可以实现接口,每个枚举实例可以有不同的接口实现。

简单代码示例


/*
    jdk1.5之后 java提供了一个关键字用于创建枚举类 enum
 */
enum Season2{
    //jdk1.5之后必须将有限个对象放在enum枚举类第一个开头位置,多个对象之间使用逗号隔开
    SPRING("春天","春暖花开"),
    SUMMER("夏天","烈日炎炎"),
    AUTUMN("秋天","秋高气爽"),
    WINTER("冬天","白雪皑皑");

    //1. 创建枚举类的属性(成员遍历),必须是作为私有常量出现
    private String name;
    private String info;

    //2. 必须将构造方法私有化,这是为了保证类的对象是有限个的目的
    private Season2(String name, String info){
        this.name = name;
        this.info = info;
    }

Java Lambda表达式实例详解

以下是几个Lambda表达式的实例,包括详细代码和解析。

实例1:使用Lambda表达式进行过滤操作

场景:假设我们有一个员工列表,需要过滤出年龄大于30岁的员工。

代码

List<Employee> employees = Arrays.asList(
    new Employee("John", 25),
    new Employee("Jane", 32),
    new Employee("Doe", 29)
);

List<Employee> olderEmployees = employees.stream()
    .filter(e -> e.getAge() > 30)
    .collect(Collectors.toList());

for(Employee e : olderEmployees) {
    System.out.println(e.getName());
}

解析

  • .filter(e -> e.getAge() > 30):这里使用了Lambda表达式来定义过滤条件,e -> e.getAge() > 30是一个简短的匿名函数,它接收一个Employee类型参数e并返回一个布尔值,表示该员工的年龄是否大于30岁。
  • .collect(Collectors.toList()):将过滤后的流收集到一个列表中。

实例2:使用Lambda表达式进行排序

场景:继续使用上面的员工列表,现在需要根据员工的姓名进行排序。

代码

List<Employee> sortedEmployees = employees.stream()
    .sorted((e1, e2 -> e1.getName().compareTo(e2.getName())))
    .collect(Collectors.toList());

for(Employee e : sortedEmployees) {
    System.out.println(e.getName());
}

解析

  • .sorted((e1, e2 -> e1.getName().compareTo(e2.getName()))):这里使用了Lambda表达式来定义排序规则,(e1, e2 -> e1.getName().compareTo(e2.getName()))是一个比较器,它接收两个Employee对象并根据他们的姓名进行排序。

实例3:使用Lambda表达式进行映射

场景:将员工列表转换为员工姓名的列表。

代码

List<String> names = employees.stream()
    .map(Employee::getName)
    .collect(Collectors.toList());

for(String name : names) {
    System.out.println(name);
}

解析

  • .map(Employee::getName):这里使用了方法引用作为Lambda表达式,Employee::getName是一个匿名函数,它接收一个Employee对象并返回该对象的姓名。

实例4:使用Lambda表达式进行消费操作

场景:对每个员工执行一个操作,例如打印员工的详细信息。

代码

employees.forEach(e -> System.out.println(e.toString()));

解析

  • .forEach(e -> System.out.println(e.toString())):这里使用了Lambda表达式来定义对每个员工执行的操作,e -> System.out.println(e.toString())是一个简短的匿名函数,它接收一个Employee类型参数e并打印该员工的详细信息。

总结

Lambda表达式和枚举是Java 8的重要特性,它们提供了更简洁、更现代的编程方式。Lambda表达式使得函数式编程在Java中成为可能,而枚举则增强了Java的常量管理能力。掌握这些特性对于编写高质量Java代码至关重要。

通过本文的详细解释和代码示例,你应该对Lambda表达式和枚举有了更深入的理解。这些特性不仅能够使代码更加简洁,还能够带来更好的性能和可维护性。在实际开发中,合理运用这些特性可以显著提升开发效率和代码质量。

posted @   bjynjj  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
点击右上角即可分享
微信分享提示