22.java8新特性

文章目录

  • `Lambda表达式`
      • Lambda表达式的基本语法
      • 示例
      • 函数接口
      • 参数与类型推断
      • 函数式接口的预定义类型
  • `自定义函数接口`
      • 例子
  • `分类`
      • 消费型接口(Consumer)
      • 供给型接口(Supplier)
      • 函数型接口(Function)
      • 断言型接口(Predicate)
      • 运算型接口(UnaryOperator)
      • 动作型接口(Runnable)
  • `消费型`
  • `供给型`
  • `函数型`
  • `断言型`
  • `方法和构造器引用`
      • 方法引用(Method Reference):
      • 构造器引用(Constructor Reference):
      • 示例:
  • `streamAPI`
    • 创建流
      • 1. 从集合创建流:
      • 2. 从数组创建流:
      • 3. 使用Stream.of创建流:
      • 4. 通过生成流的方式:
        • a. 使用 Stream.iterate:
        • b. 使用 Stream.generate:
      • 5. 从文件创建流:
      • 6. 使用特殊值创建流:
    • 中间操作
      • 1. **Filter(过滤):**
      • 2. **Map(映射):**
      • 3. **FlatMap(扁平映射):**
      • 4. **Distinct(去重):**
      • 5. **Sorted(排序):**
      • 6. **Peek(查看):**
  • 终止操作
      • 1. **forEach(遍历):**
      • 2. **count(计数):**
      • 3. **collect(收集):**
      • 4. **reduce(归约):**
      • 5. **anyMatch、allMatch、noneMatch(匹配):**
      • 6. **min、max(最小值、最大值):**
  • Optional 类
      • 创建 Optional 对象:
      • 获取值:
      • 判断是否包含值:
      • 使用 map 处理值:
      • 使用过滤器过滤值:
      • 使用 flatMap 处理嵌套的 Optional:

Lambda表达式

Lambda表达式是Java 8引入的一项重要特性,它为Java语言引入了函数式编程的概念。Lambda表达式使得代码更为简洁、可读,并且支持更灵活的编程方式。下面是Lambda表达式的一些详细信息:

Lambda表达式的基本语法

Lambda表达式的基本语法如下:

(parameters) -> expression

或者

(parameters) -> { statements; }
  • parameters:参数列表,类似于方法的参数列表,可以为空或非空。
  • ->:Lambda运算符,分隔参数列表和Lambda表达式的主体。
  • expression:Lambda表达式的执行体,可以是单个表达式。
  • { statements; }:Lambda表达式的执行体,可以是一个代码块,包含多个语句。

示例

下面是一个简单的Lambda表达式示例,实现了一个简单的函数接口:

// 使用匿名内部类
Runnable runnable1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello from anonymous class!");
    }
};

// 使用Lambda表达式
Runnable runnable2 = () -> System.out.println("Hello from Lambda!");

// 调用
runnable1.run();
runnable2.run();

函数接口

Lambda表达式需要与函数接口(Functional Interface)一起使用。函数接口是只包含一个抽象方法的接口,可以使用@FunctionalInterface注解来确保它是函数接口。Lambda表达式可以与任何函数接口匹配。

@FunctionalInterface
interface MyFunctionalInterface {
    void myMethod();
}

MyFunctionalInterface myLambda = () -> System.out.println("Hello from Lambda!");

// 调用
myLambda.myMethod();

参数与类型推断

Lambda表达式可以自动推断参数的类型,也可以省略参数的类型声明。例如:

// 无参数的Lambda表达式
() -> System.out.println("Hello");

// 单参数的Lambda表达式
x -> x * x;

// 多参数的Lambda表达式
(x, y) -> x + y;

函数式接口的预定义类型

Java 8引入了一些预定义的函数式接口,如ConsumerPredicateFunction等。这些接口可以用于Lambda表达式的参数类型。

// Consumer接口,接受一个参数,无返回值
Consumer<String> printUpperCase = str -> System.out.println(str.toUpperCase());

// Predicate接口,接受一个参数,返回boolean值
Predicate<Integer> isEven = num -> num % 2 == 0;

// Function接口,接受一个参数,返回一个结果
Function<Integer, String> intToString = num -> String.valueOf(num);

Lambda表达式是Java 8引入的一个强大功能,它使得编写函数式风格的代码更为容易。通过使用Lambda表达式,可以实现更简洁、清晰、灵活的代码。

自定义函数接口

在Java中,可以通过自定义接口并使用@FunctionalInterface注解来创建自定义的函数接口。一个函数接口只能有一个抽象方法,但可以包含多个默认方法或静态方法。下面是一个简单的自定义函数接口的例子:

@FunctionalInterface
interface MyCustomFunctionalInterface {
    // 抽象方法
    void myMethod();

    // 默认方法
    default void defaultMethod() {
        System.out.println("Default method in the interface");
    }

    // 静态方法
    static void staticMethod() {
        System.out.println("Static method in the interface");
    }
}

在上面的例子中:

  • MyCustomFunctionalInterface是一个自定义的函数接口。
  • myMethod是该接口的抽象方法,只包含一个方法。
  • defaultMethod是一个默认方法,可以在接口中提供默认的实现。
  • staticMethod是一个静态方法,可以在接口中提供静态方法。

你可以使用这个自定义函数接口并通过Lambda表达式来实现其抽象方法:

MyCustomFunctionalInterface myLambda = () -> System.out.println("Hello from myMethod");

// 调用抽象方法
myLambda.myMethod();

// 调用默认方法
myLambda.defaultMethod();

// 调用静态方法
MyCustomFunctionalInterface.staticMethod();

这样,你就创建了一个自定义的函数接口,并通过Lambda表达式实现了它的抽象方法。这种方式使得你能够定义自己的函数式接口,以便在项目中更灵活地使用Lambda表达式。

例子

当你需要执行某种操作时,你可以定义一个适合Lambda表达式的自定义函数接口。下面是一个简单的例子,假设你想要定义一个能够执行两个整数相加的函数接口:

@FunctionalInterface
interface MyAddition {
    int add(int a, int b);
}

这是一个非常简单的函数接口,只有一个抽象方法 add,接受两个整数参数并返回它们的和。现在,你可以使用这个自定义函数接口来创建Lambda表达式,并执行加法操作:

public class CustomFunctionalInterfaceExample {
    public static void main(String[] args) {
        // 使用Lambda表达式实现加法操作
        MyAddition addition = (a, b) -> a + b;

        // 调用add方法
        int result = addition.add(5, 3);

        System.out.println("Result: " + result);
    }
}

在这个例子中,MyAddition函数接口充当了一个加法操作的定义,Lambda表达式 (a, b) -> a + b 实现了 add 方法的具体逻辑。最后,通过调用 add 方法,你可以得到两个整数的和,并输出结果。这是一个简单的例子,但它展示了如何创建和使用自定义的函数接口。

分类

函数式接口可以分为几个主要的类别,这些类别基于接口中的抽象方法数量和签名。在Java中,函数式接口通常用于支持Lambda表达式。以下是一些常见的函数式接口分类:

  1. 消费型接口(Consumer)

    • 描述:接受一个参数,但没有返回值。
    • 示例Consumer
  2. 供给型接口(Supplier)

    • 描述:不接受任何参数,但有返回值。
    • 示例Supplier
  3. 函数型接口(Function)

    • 描述:接受一个参数,并返回一个结果。
    • 示例Function
  4. 断言型接口(Predicate)

    • 描述:接受一个参数,并返回一个布尔值。
    • 示例Predicate
  5. 运算型接口(Operator)

    • 描述:接受一个参数,进行一些操作,并返回结果(通常用于修改输入参数)。
    • 示例UnaryOperator(一元运算符),BinaryOperator(二元运算符)。
  6. 功能组合接口

    • 描述:组合了多个基本函数接口,通常用于构建更复杂的操作。
    • 示例BiFunction(接受两个参数并返回结果),UnaryOperator(一元运算符)等。
  7. 动作型接口(Runnable、Callable等)

    • 描述:通常不接受参数或者参数没有语义上的意义,也没有返回值。
    • 示例RunnableCallable
  8. 特化的函数式接口

    • 描述:Java 8引入了一系列特化的函数式接口,如IntConsumerLongFunction等,用于处理特定的数据类型,避免装箱拆箱的性能开销。

示例代码:

// Consumer接口
Consumer<String> printUpperCase = str -> System.out.println(str.toUpperCase());

// Supplier接口
Supplier<Integer> randomNumber = () -> (int) (Math.random() * 100);

// Function接口
Function<Integer, String> intToString = num -> String.valueOf(num);

// Predicate接口
Predicate<Integer> isEven = num -> num % 2 == 0;

// UnaryOperator接口
UnaryOperator<Integer> square = x -> x * x;

// BinaryOperator接口
BinaryOperator<Integer> add = (a, b) -> a + b;

这些接口提供了不同类型的操作,使得在使用Lambda表达式时更加灵活和方便。选择适当的函数式接口取决于你希望执行的操作的特性。
好的,我将为你提供一些Lambda表达式的练习,涵盖不同类型的函数式接口。每个练习都有一个简单的任务,你可以使用Lambda表达式来实现。请注意,这些练习旨在帮助你熟悉Lambda表达式的不同应用场景。

消费型接口(Consumer)

  1. 创建一个Consumer,用于打印字符串的大写形式。
Consumer<String> printUpperCase = // Lambda表达式实现
printUpperCase.accept("hello"); // 输出: HELLO

供给型接口(Supplier)

  1. 创建一个Supplier,用于生成一个随机整数。
Supplier<Integer> randomNumber = // Lambda表达式实现
int number = randomNumber.get();
System.out.println("Random Number: " + number);

函数型接口(Function)

  1. 创建一个Function,将整数转换为对应的星期几字符串。
Function<Integer, String> intToDay = // Lambda表达式实现
String day = intToDay.apply(1); // 输出: Monday

断言型接口(Predicate)

  1. 创建一个Predicate,判断字符串是否为空。
Predicate<String> isNullOrEmpty = // Lambda表达式实现
boolean result = isNullOrEmpty.test("Hello");
System.out.println("Is Null or Empty: " + result); // 输出: false

运算型接口(UnaryOperator)

  1. 创建一个UnaryOperator,用于计算一个整数的平方。
UnaryOperator<Integer> square = // Lambda表达式实现
int result = square.apply(5); // 输出: 25

动作型接口(Runnable)

  1. 创建一个Runnable,用于在控制台打印一条简单的消息。
Runnable printMessage = // Lambda表达式实现
printMessage.run(); // 输出: Hello, Lambda!

这些练习旨在帮助你练习不同类型的函数式接口的使用,以及如何使用Lambda表达式来简化代码。完成这些练习后,你应该对Lambda表达式在不同场景下的应用有更好的理解。

消费型

在Java 8中,消费型接口通常是指只接受参数而没有返回值的函数接口。一个常见的例子是Consumer接口。以下是一个简单的Java 8消费型接口练习示例:

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

public class ConsumerExample {

    public static void main(String[] args) {
        // 创建一个字符串列表
        List<String> languages = Arrays.asList("Java", "Python", "JavaScript", "C#", "Ruby");

        // 使用forEach方法遍历列表,并使用Consumer接口打印每个元素
        System.out.println("Printing elements using Consumer:");
        forEachWithConsumer(languages, (String language) -> {
            System.out.println(language);
        });

        // 使用匿名内部类实现Consumer接口
        System.out.println("\nPrinting elements using anonymous class:");
        forEachWithConsumer(languages, new Consumer<String>() {
            @Override
            public void accept(String language) {
                System.out.println(language);
            }
        });
    }

    // 使用Consumer接口的forEach方法来遍历列表
    private static <T> void forEachWithConsumer(List<T> list, Consumer<T> consumer) {
        for (T element : list) {
            consumer.accept(element);
        }
    }
}

在这个示例中,我们创建了一个字符串列表(languages),然后使用forEachWithConsumer方法遍历列表,并通过Consumer接口打印每个元素。可以看到,通过Lambda表达式或匿名内部类,我们可以方便地传递不同的消费型接口实现来执行不同的操作。

供给型

在Java 8中,供给型接口通常是指无参数但有返回值的函数接口。一个常见的例子是Supplier接口。以下是一个简单的Java 8供给型接口练习示例:

import java.util.function.Supplier;

public class SupplierExample {

    public static void main(String[] args) {
        // 使用Supplier接口生成随机数
        Supplier<Double> randomNumberSupplier = () -> Math.random();

        // 打印生成的随机数
        System.out.println("Random Number: " + randomNumberSupplier.get());

        // 使用Supplier接口生成当前时间戳
        Supplier<Long> currentTimeSupplier = System::currentTimeMillis;

        // 打印当前时间戳
        System.out.println("Current Time: " + currentTimeSupplier.get());
    }
}

在这个示例中,我们首先创建了一个Supplier接口的实现,通过Lambda表达式生成一个随机数。然后,我们使用get方法获取随机数的值并打印出来。

接着,我们创建另一个Supplier接口的实现,这次使用方法引用(System::currentTimeMillis)来获取当前时间戳。同样,我们通过get方法获取当前时间戳的值并打印出来。

这个示例演示了如何使用供给型接口来生成不同类型的值,可以根据实际需求自定义Supplier接口的实现。

函数型

在Java 8中,函数型接口是指只有一个抽象方法的接口,可以使用Lambda表达式来实现。java.util.function 包中提供了许多内置的函数型接口,比如FunctionPredicateUnaryOperator等。以下是一个简单的Java 8函数型接口练习示例:

import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;

public class FunctionInterfaceExample {

    public static void main(String[] args) {
        // 使用Function接口将字符串转为大写
        Function<String, String> toUpperCaseFunction = str -> str.toUpperCase();

        String inputString = "hello world";

        // 打印转换前的字符串
        System.out.println("Original String: " + inputString);

        // 使用Function接口转换字符串为大写
        String upperCaseString = toUpperCaseFunction.apply(inputString);

        // 打印转换后的字符串
        System.out.println("Upper Case String: " + upperCaseString);

        // 使用Predicate接口判断一个数字是否为正数
        Predicate<Integer> isPositive = num -> num > 0;

        int number = 42;

        // 判断数字是否为正数
        System.out.println("Is " + number + " positive? " + isPositive.test(number));

        // 使用UnaryOperator接口对一个数字进行平方操作
        UnaryOperator<Integer> square = x -> x * x;

        int originalNumber = 7;

        // 打印平方前的数字
        System.out.println("Original Number: " + originalNumber);

        // 使用UnaryOperator接口进行平方操作
        int squaredNumber = square.apply(originalNumber);

        // 打印平方后的数字
        System.out.println("Squared Number: " + squaredNumber);
    }
}

在这个示例中,我们使用了Function接口将字符串转为大写,Predicate接口判断一个数字是否为正数,以及UnaryOperator接口对一个数字进行平方操作。通过Lambda表达式实现了这些接口的抽象方法。函数型接口的使用使得代码更为简洁和灵活。

断言型

在Java 8中,断言型接口通常是指具有布尔类型返回值的函数接口,主要用于在代码中进行条件判断。一个常见的例子是Predicate接口。以下是一个简单的Java 8断言型接口练习示例:

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

public class PredicateExample {

    public static void main(String[] args) {
        // 创建一个整数列表
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        // 使用Predicate接口判断数字是否为偶数,并打印结果
        System.out.println("Even numbers:");
        evaluate(numbers, (Integer number) -> number % 2 == 0);

        // 使用Predicate接口判断数字是否大于 5,并打印结果
        System.out.println("\nNumbers greater than 5:");
        evaluate(numbers, (Integer number) -> number > 5);
    }

    // 使用Predicate接口的test方法来进行条件判断
    private static <T> void evaluate(List<T> list, Predicate<T> predicate) {
        for (T element : list) {
            if (predicate.test(element)) {
                System.out.print(element + " ");
            }
        }
    }
}

在这个示例中,我们创建了一个整数列表(numbers),然后使用 evaluate 方法通过 Predicate 接口进行条件判断。首先,我们使用 Predicate 接口判断数字是否为偶数,然后判断数字是否大于 5。在 evaluate 方法中,我们通过调用 predicate.test(element) 执行断言,如果返回 true,则打印相应的元素。

这个示例演示了如何使用断言型接口来进行灵活的条件判断,可以根据实际需求自定义 Predicate 接口的实现。

方法和构造器引用

在Java 8中,方法引用和构造器引用是Lambda表达式的一种简化形式,用于更简洁地表示特定类型的函数式接口的实现。它们提供了一种更紧凑的语法,以便更清晰地表达代码的意图。

方法引用(Method Reference):

方法引用允许你通过方法的名称来引用它。它提供了一种简化Lambda表达式的方式,可以用更紧凑的语法来调用已经存在的方法。

有四种主要的方法引用形式:

  1. 静态方法引用:

    // 例如,假设有一个静态方法
    ClassName::staticMethodName;
    
  2. 实例方法引用:

    // 例如,假设有一个实例方法,通过对象实例引用
    instance::instanceMethodName;
    
  3. 类名引用一个实例方法:

    // 例如,假设有一个实例方法,通过类名引用,适用于静态上下文
    ClassName::instanceMethodName;
    
  4. 类名引用一个构造方法:

    // 例如,通过类名引用构造方法
    ClassName::new;
    

构造器引用(Constructor Reference):

构造器引用是一种特殊的方法引用,用于通过构造方法创建新对象。

构造器引用的语法如下:

ClassName::new;

这将引用 ClassName 类的构造方法,用于创建新的对象。

示例:

以下是一个使用方法引用和构造器引用的简单示例:

import java.util.Arrays;
import java.util.List;

class Person {
    String name;

    Person(String name) {
        this.name = name;
    }

    static void printName(Person person) {
        System.out.println(person.name);
    }
}

public class MethodAndConstructorReferenceExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

        // 使用静态方法引用
        names.forEach(Person::printName);

        // 使用构造器引用
        List<Person> persons = Arrays.asList(
                new Person("Alice"),
                new Person("Bob"),
                new Person("Charlie")
        );
        persons.forEach(Person::printName);
    }
}

在上面的例子中,Person::printName 是一个静态方法引用,Person::new 是一个构造器引用。这两种引用形式都使代码更加简洁、易读。

streamAPI

创建流

Java 8引入了流(Stream)的概念,它是一种用于处理集合数据的新抽象。流允许你通过一系列的操作来处理集合,这些操作可以是中间操作或终端操作。

以下是如何创建流的一些方法:

1. 从集合创建流:

List<String> myList = Arrays.asList("Java", "Python", "JavaScript", "C#", "Ruby");

// 从集合创建流
Stream<String> streamFromList = myList.stream();

2. 从数组创建流:

String[] array = {"Java", "Python", "JavaScript", "C#", "Ruby"};

// 从数组创建流
Stream<String> streamFromArray = Arrays.stream(array);

3. 使用Stream.of创建流:

Stream<String> streamOfValues = Stream.of("Java", "Python", "JavaScript", "C#", "Ruby");

4. 通过生成流的方式:

a. 使用 Stream.iterate:
// 从0开始,每次加2,生成无限流
Stream<Integer> infiniteStream = Stream.iterate(0, n -> n + 2);
b. 使用 Stream.generate:
// 生成5个随机数的流
Stream<Double> randomNumbers = Stream.generate(Math::random).limit(5);

5. 从文件创建流:

// 从文件创建流,每行作为流的元素
Path filePath = Paths.get("path/to/file.txt");
Stream<String> lines = Files.lines(filePath);

6. 使用特殊值创建流:

// 创建空流
Stream<String> emptyStream = Stream.empty();

// 创建包含单个元素的流
Stream<String> singleElementStream = Stream.of("Java");

// 创建包含重复元素的流
Stream<String> repeatedElementStream = Stream.generate(() -> "Java").limit(5);

这些是一些创建流的基本方法。创建流后,你可以使用各种中间操作和终端操作来对流进行处理。例如,filtermapflatMap 是一些中间操作,而 forEachcollectreduce 是一些终端操作。

中间操作

中间操作是在流上执行的操作,这些操作不会产生最终的结果,而是返回一个新的流,以便可以将多个中间操作链接在一起。以下是一些中间操作的示例:

1. Filter(过滤):

过滤操作通过给定的条件过滤流中的元素:

List<String> myList = Arrays.asList("Java", "Python", "JavaScript", "C#", "Ruby");

// 过滤出长度大于3的元素
Stream<String> filteredStream = myList.stream().filter(s -> s.length() > 3);

2. Map(映射):

映射操作用于对流中的每个元素应用函数:

List<String> myList = Arrays.asList("Java", "Python", "JavaScript", "C#", "Ruby");

// 将每个字符串转换为大写
Stream<String> upperCaseStream = myList.stream().map(String::toUpperCase);

3. FlatMap(扁平映射):

flatMap 将每个元素映射为一个流,然后将这些流连接成一个流:

List<List<Integer>> numbers = Arrays.asList(
    Arrays.asList(1, 2, 3),
    Arrays.asList(4, 5, 6),
    Arrays.asList(7, 8, 9)
);

// 将嵌套列表平铺成一个流
Stream<Integer> flatMapStream = numbers.stream().flatMap(List::stream);

4. Distinct(去重):

去重操作用于移除流中的重复元素:

List<String> myList = Arrays.asList("Java", "Python", "JavaScript", "C#", "Ruby", "Java");

// 去除重复元素
Stream<String> distinctStream = myList.stream().distinct();

5. Sorted(排序):

排序操作用于对流中的元素进行排序:

List<String> myList = Arrays.asList("Java", "Python", "JavaScript", "C#", "Ruby");

// 对元素按字母顺序排序
Stream<String> sortedStream = myList.stream().sorted();

6. Peek(查看):

peek 操作用于查看流中的元素,常用于调试:

List<String> myList = Arrays.asList("Java", "Python", "JavaScript", "C#", "Ruby");

// 打印每个元素并返回原始流
Stream<String> peekStream = myList.stream().peek(System.out::println);

这些中间操作允许你以流畅的方式对数据进行转换和处理,创建一个由多个操作链接而成的流水线。最终,通过终端操作,你可以获取结果或触发流的处理。

终止操作

终端操作是流操作的最终步骤,它们会触发流的处理,并产生一个最终的结果或副作用。以下是一些终端操作的示例:

1. forEach(遍历):

forEach 用于遍历流中的元素:

List<String> myList = Arrays.asList("Java", "Python", "JavaScript", "C#", "Ruby");

// 遍历并打印每个元素
myList.stream().forEach(System.out::println);

2. count(计数):

count 用于计算流中的元素数量:

List<String> myList = Arrays.asList("Java", "Python", "JavaScript", "C#", "Ruby");

// 计算流中的元素数量
long count = myList.stream().count();
System.out.println("Number of elements: " + count);

3. collect(收集):

collect 用于将流中的元素收集到一个集合或其他数据结构中:

List<String> myList = Arrays.asList("Java", "Python", "JavaScript", "C#", "Ruby");

// 将元素收集到一个列表中
List<String> collectedList = myList.stream().collect(Collectors.toList());

4. reduce(归约):

reduce 用于将流中的元素归约为一个值:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 将元素相加,得到总和
Optional<Integer> sum = numbers.stream().reduce((a, b) -> a + b);
System.out.println("Sum: " + sum.orElse(0));

5. anyMatch、allMatch、noneMatch(匹配):

这些操作用于检查流中的元素是否匹配给定的条件:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 是否存在大于3的元素
boolean anyGreaterThanThree = numbers.stream().anyMatch(n -> n > 3);

// 是否所有元素都大于3
boolean allGreaterThanThree = numbers.stream().allMatch(n -> n > 3);

// 是否不存在小于0的元素
boolean noneLessThanZero = numbers.stream().noneMatch(n -> n < 0);

6. min、max(最小值、最大值):

minmax 用于获取流中的最小值和最大值:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// 获取最小值
Optional<Integer> min = numbers.stream().min(Integer::compareTo);

// 获取最大值
Optional<Integer> max = numbers.stream().max(Integer::compareTo);

这些是一些常见的终端操作,它们用于触发流的处理并生成最终的结果或副作用。在使用流时,选择适当的终端操作取决于你的具体需求。

Optional 类

Optional 类是 Java 8 引入的一个用于处理可能为 null 的值的容器类。它旨在减少在代码中出现的 null 引用,以避免空指针异常。

Optional 类的主要目标是通过显式声明一个值可能为空,从而鼓励程序员更加谨慎地处理可能为 null 的情况,以减少空指针异常的风险。

以下是 Optional 类的一些基本用法:

创建 Optional 对象:

  1. of 方法:

    Optional<String> optionalValue = Optional.of("Hello, World!");
    
  2. ofNullable 方法:

    String value = // some value, may be null
    Optional<String> optionalValue = Optional.ofNullable(value);
    
  3. empty 方法:

    Optional<String> emptyOptional = Optional.empty();
    

获取值:

  1. get 方法:

    Optional<String> optionalValue = Optional.of("Hello, World!");
    String value = optionalValue.get();
    

    注意:使用 get 方法时,如果 Optional 包含了 null 值,将抛出 NoSuchElementException

  2. orElse 方法:

    Optional<String> optionalValue = Optional.ofNullable(null);
    String result = optionalValue.orElse("Default Value");
    

    如果 optionalValuenull,则返回指定的默认值。

  3. orElseGet 方法:

    Optional<String> optionalValue = Optional.ofNullable(null);
    String result = optionalValue.orElseGet(() -> generateDefaultValue());
    

    如果 optionalValuenull,则通过 orElseGet 方法中提供的 Supplier 生成默认值。

判断是否包含值:

Optional<String> optionalValue = Optional.of("Hello, World!");

// 判断是否包含值
boolean hasValue = optionalValue.isPresent();

使用 map 处理值:

Optional<String> optionalValue = Optional.of("Hello, World!");

// 如果值存在,将其转换为大写
Optional<String> upperCaseValue = optionalValue.map(String::toUpperCase);

使用过滤器过滤值:

Optional<String> optionalValue = Optional.of("Hello, World!");

// 过滤值,只有满足条件的值才会保留
Optional<String> filteredValue = optionalValue.filter(value -> value.contains("Hello"));

使用 flatMap 处理嵌套的 Optional:

Optional<String> optionalValue = Optional.of("Hello, World!");

// 如果值存在,将其转换为大写,然后使用 flatMap 处理嵌套的 Optional
Optional<String> result = optionalValue.flatMap(value -> Optional.ofNullable(value.toUpperCase()));

Optional 类提供了一种更安全、更规范的处理可能为 null 的值的方式。在编写代码时,考虑使用 Optional 可以提高代码的可读性和可维护性。

你可能感兴趣的:(java)