Lambda表达式与StreamAPI
Lambda表达式与StreamAPI
Author: Msuenb
Date: 2023-02022
函数式编程思想
面向对象过分强调"必须通过对象的形式来做事情",而函数式思想则是尽量忽略面向对象的复杂语法——强调做什么,而不是谁来做。
JDK8中引入了Lambda表达式,Java也开始支持函数式编程。
public class TestLambda {
@Test
public void test1(){
String[] arr = {"hello", "java", "good", "world", "ok"};
// 把上面的字符串按照长短排序,从短到长
Arrays.sort(arr, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// return o1.length()-o2.length();
return Integer.compare(o1.length(), o2.length());
}
});
/*
上面的写法使用了匿名内部类,既声明了一个类,又创建了一个对象。
创建这个对象的目的是为了给sort方法的第二个形参c赋值。
声明匿名内部类的目的是为了重写public int compare(String o1, String o2)
*/
System.out.println(Arrays.toString(arr));
}
@Test
public void test2(){
String[] arr = {"hello", "java", "good", "world", "ok"};
//这个需求中我们关心的是什么? 如何比较两个字符串的大小,至于对象不重要
Arrays.sort(arr, (String o1, String o2) -> { return Integer.compare(o1.length(), o2.length()); });
System.out.println(Arrays.toString(arr));
}
}
这里并不希望创建一个匿名内部类对象,只是为了做这件事情而不得不创建一个对象。真正希望做的事情是:将compareTo
方法体内的代码传递给sort
方法知晓。
传递一段代码——这才是真正的目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。使用Lambda表达式不再有“不得不创建接口对象”的束缚,就是这么简单!
Lambda表达式
Lambda表达式是用来给"函数式接口"的变量或形参赋值用的。其实本质上,Lambda表达式是用于实现"函数式接口"的抽象方法的语法格式,或是给函数式接口的变量传递一段实现抽象方法的代码。
Lambda表达式语法格式:
(形参列表) -> {Lambda体}
语法格式说明:
(形参列表)
:它就是你要赋值的函数式接口的抽象方法的(形参列表){Lambda体}
:就是实现这个抽象方法的方法体->
:称为Lambda操作符(减号和大于号中间不能有空格)
使用演示:
@Test
public void test1(){
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello, World!");
}
};
}
@Test
public void test2(){
Runnable r = () -> {
System.out.println("Hello, World");
};
}
Lambda 表达式的简化:
- 当
{Lambda体}
中只有一句话时,可以省略{};
- 当
{Lambda体}
中只有一句语句时,并且这个语句还是一个return语句,那么{};
和return
可以省略。 - 当Lambda表达式
(形参列表)
的类型已知,获取根据泛型规则可以自动推断,那么(形参列表)
的数据类型可以省略。 - 当Lambda表达式
(形参列表)
的形参个数只有一个,并且类型已知或可以自动推断,可以只写形参名。 - 当Lambda表达式
(形参列表)
是空参时,() 不能省略
@Test
public void test2(){
Runnable r = () -> System.out.println("Hello, World");
}
@Test
public void test6(){
String[] arr = {"hello", "java", "good", "world", "ok"};
//把上面的字符串按照长短排序,从短到长
Arrays.sort(arr, (o1, o2) -> Integer.compare(o1.length(), o2.length()));
System.out.println(Arrays.toString(arr));
}
函数式接口
Java8在java.util.function新增了很多函数式接口:主要分为四大类,消费型、供给型、判断型、功能型。基本可以满足平时的开发需求,当然也可以自定义函数式接口。
-
消费型接口
消费型接口的抽象方法特点:有形参,但是返回值类型是void
序号 接口名 抽象方法 描述 1 Consumer void accept(T t) 接收一个对象用于完成功能 2 BiConsumer<T,U> void accept(T t, U u) 接收两个对象用于完成功能 3 DoubleConsumer void accept(double value) 接收一个double值 4 IntConsumer void accept(int value) 接收一个int值 5 LongConsumer void accept(long value) 接收一个long值 6 ObjDoubleConsumer void accept(T t, double value) 接收一个对象和一个double值 7 ObjIntConsumer void accept(T t, int value) 接收一个对象和一个int值 8 ObjLongConsumer void accept(T t, long value) 接收一个对象和一个long值 在JDK1.8中,java.lang.Iterable接口中增加了一个默认方法:
public default void forEach(Consumer<? super T> action)
该方法功能是遍历Collection集合,并将传递给action参数的操作代码应用在每一个元素上。
@Test public void consumerTest() { List<String> list = Arrays.asList("java","c","python","c++","VB","C#"); list.forEach(s -> System.out.println(s)); }
-
供给型接口
供给型接口的抽象方法特点:无参,但是有返回值
序号 接口名 抽象方法 描述 1 Supplier T get() 返回一个对象 2 BooleanSupplier boolean getAsBoolean() 返回一个boolean值 3 DoubleSupplier double getAsDouble() 返回一个double值 4 IntSupplier int getAsInt() 返回一个int值 5 LongSupplier long getAsLong() 返回一个long值 @Test public void supplierTest() { Supplier<String> supplier = () -> "hello, world"; System.out.println(supplier.get()); }
-
判断型接口
判断型接口的抽象方法特点:有参,但是返回值类型是boolean结果。
序号 接口名 抽象方法 描述 1 Predicate boolean test(T t) 接收一个对象 2 BiPredicate<T,U> boolean test(T t, U u) 接收两个对象 3 DoublePredicate boolean test(double value) 接收一个double值 4 IntPredicate boolean test(int value) 接收一个int值 5 LongPredicate boolean test(long value) 接收一个long值 JDK1.8中,Collecton接口增加了一下方法:
public default boolean removeIf(Predicate<? super E> filter)
用于删除集合中满足filter指定的条件判断的。
@Test public void removeIfTest() { ArrayList<String> strings = new ArrayList<>(); strings.add("hello"); strings.add("java"); strings.add("world"); strings.add("ok"); strings.add("yes"); System.out.println("删除之前:"); strings.forEach(s -> System.out.print(s + ", ")); // 要求删除包含o字母的元素 strings.removeIf(s -> s.contains("o")); System.out.println("\n删除之后:"); strings.forEach(s -> System.out.print(s + ", ")); }
-
功能型接口
功能型接口的抽象方法特点:既有参数又有返回值
序号 接口名 抽象方法 描述 1 Function<T,R> R apply(T t) 接收一个T类型对象,返回一个R类型对象结果 2 UnaryOperator T apply(T t) 接收一个T类型对象,返回一个T类型对象结果 3 DoubleFunction R apply(double value) 接收一个double值,返回一个R类型对象 4 IntFunction R apply(int value) 接收一个int值,返回一个R类型对象 5 LongFunction R apply(long value) 接收一个long值,返回一个R类型对象 6 ToDoubleFunction double applyAsDouble(T value) 接收一个T类型对象,返回一个double 7 ToIntFunction int applyAsInt(T value) 接收一个T类型对象,返回一个int ... ... JDK8中,java.util.List接口新增了一个方法:
default void replaceAll(UnaryOperator operator)
将该列表的每个元素替换为将该运算符应用于该元素的结果。
@Test public void funTest() { ArrayList<String> list = new ArrayList<>(); list.add("hello"); list.add("java"); list.add("world"); // 使用Lambda表达式实现Function<T,R>接口的子接口UnaryOperator<T> // 可以实现将一个字符串首字母转为大写的功能。 list.replaceAll(s -> s.toUpperCase()); list.forEach(s -> System.out.print(s + ", ")); }
自定义函数式接口
只要确保接口中有且仅有一个抽象方法即可
例如:声明一个转换器 Convertor<T,R>,包含抽象方法change,可以将参数转换为另一个值,并返回结果。其中T是参数类型,R是返回值类型。
interface Convertor<T, R> {
R change(T t);
}
public class CustomFunItfTest {
public static void main(String[] args) {
Convertor<String, Character> convertor = s -> s.charAt(0);
Character charHello = convertor.change("hello");
System.out.println(charHello);
}
}
StreamAPI
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。
Stream特点
Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream 是 Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,负责存储数据,Stream流讲的是计算,负责处理数据!”
- Stream 自己不会存储元素
- Stream 不会改变源对象。每次处理都会返回一个持有结果的新Stream
- Stream 操作是延迟执行的。这意味着它们会等到需要结果时才执行
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Stream;
public class TestStream {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "hello", "java", "world", "yes", "no");
// 1.创建Stream
Stream<String> stream = list.stream();
// 2.加工处理
stream = stream.filter(s -> s.contains("o"));
stream = stream.peek(s -> System.out.print(s + ", "));
// 3.结束处理
long count = stream.count(); // 没有这一步 前面不会执行
System.out.println(count);
System.out.println(list); // 不会修改数据
}
}
Stream 操作步骤
- 创建Stream:通过一个数据源(集合、数组),获取一个流
- 中间操作:每次处理都会返回一个持有结果的新Stream,即中间操作的方法仍返回Stream类型的对象,因此中间操作可以是个操作链,但在终结操作前,并不会真正执行。
- 终止操作:终止操作的返回值类型就不再是Stream了,一旦执行终止操作,就执行中间操作链,产生结果并结束Stream。
创建StreamAPI
-
创建 Stream 方式一:通过集合
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
-
public default Stream stream() : 返回一个顺序流
-
public default Stream parallelStream() : 返回一个并行流
-
-
创建 Stream 方式二:通过数组
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
-
public static Stream stream(T[] array): 返回一个流。重载形式,能够处理对应基本类型的数组:
-
public static IntStream stream(int[] array):返回一个整型数据流
-
public static LongStream stream(long[] array):返回一个长整型数据流
-
public static DoubleStream stream(double[] array):返回一个浮点型数据流
-
-
创建 Stream 方式三:通过Stream的 of()
可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
- public static Stream of(T... values) : 返回一个顺序流
-
创建 Stream方式四:创建无限流
可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
-
public static Stream iterate(final T seed, final UnaryOperator f):返回一个无限流
-
public static Stream generate(Supplier s) :返回一个无限流
-
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Stream;
public class TestCreateStream {
@Test
public void test01() {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "hello", "java", "world", "yes", "no");
Stream<String> stream = list.stream();
}
@Test
public void test02() {
String[] str = {"hello", "java", "world", "ok", "yes"};
Arrays.stream(str);
}
@Test
public void test03() {
Stream<String> stream = Stream.of("hello", "java", "world", "ok", "yes");
}
@Test
public void test04() {
// 创建无线流
Stream<Double> stream = Stream.generate(() -> Math.random());
// 结束 stream
stream.forEach(d -> System.out.println(d));
}
@Test
public void test05() {
Stream stream = Stream.iterate(1, t -> t + 1);
// 结束stream
stream.forEach(t -> System.out.println(t));
}
}
中间操作API
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
序号 | 方 法 | 描 述 |
---|---|---|
1 | Stream filter(Predicate p) | 接收 Lambda , 从流中排除某些元素 |
2 | Stream distinct() | 筛选,通过流所生成元素的equals() 去除重复元素 |
3 | Stream limit(long maxSize) | 截断流,使其元素不超过给定数量 |
4 | Stream skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。 若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
5 | Stream peek(Consumer action) | 接收Lambda,对流中的每个数据执行Lambda体操作 |
6 | Stream sorted() | 产生一个新流,其中按自然顺序排序 |
7 | Stream sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 |
8 | Stream map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上, 并将其映射成一个新的元素。 |
9 | Stream mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上, 产生一个新的 DoubleStream。 |
10 | Stream mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上, 产生一个新的 IntStream。 |
11 | Stream mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上, 产生一个新的 LongStream。 |
12 | Stream flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流, 然后把所有流连接成一个流 |
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestMiddle {
@Test
public void test01() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5); // 获取stream 必须要
// 没有中间处理操作
stream.forEach(t -> System.out.println(t)); // 终结操作必须要
}
@Test
public void test02() {
// 操作链
Stream.of(5, 2, 1, 3, 6, 4, 7, 9, 0)
.filter(t -> t % 2 == 0)
.sorted((t1, t2) -> t1 - t2)
.forEach(t -> System.out.println(t));
}
@Test
public void test03() {
// 把一组数字中,不同的数字筛选出来,打印这些数字,并且把这些不同的数字从小到大收集到List中
List<Integer> list = Stream.of(9, 6, 3, 2, 1, 5, 8, 7, 4, 1, 2, 5, 9, 4, 0)
.distinct() // 去重
.sorted() // 排序
.peek(t -> System.out.print(t + " "))
.collect(Collectors.toList());
}
@Test
public void test04() {
// 随机产生一些[0,100)的整数,取出前10个
Random random = new Random();
List<Integer> list = Stream.generate(() -> random.nextInt(100))
.limit(10)
.collect(Collectors.toList());
System.out.println(list);
}
@Test
public void test05() {
// 取出第三个元素
Stream.of("hello", "java", "world", "ok", "yes")
.skip(2)
.limit(1)
.forEach(t -> System.out.println(t));
}
}
终结操作API
终结操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void。流进行了终止操作后,不能再次使用。
序号 | 返回值类型 | 方法 | 描述 |
---|---|---|---|
1 | boolean | allMatch(Predicate p) | 检查是否匹配所有元素 |
2 | boolean | anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
3 | boolean | noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
4 | Optional | findFirst() | 返回第一个元素 |
5 | Optional | findAny() | 返回当前流中的任意元素 |
6 | long | count() | 返回流中元素总数 |
7 | Optional | max(Comparator c) | 返回流中最大值 |
8 | Optional | min(Comparator c) | 返回流中最小值 |
9 | void | forEach(Consumer c) | 迭代 |
10 | T | reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 T |
11 | U | reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 Optional |
12 | R | collect(Collector c) | 将流转换为其他形式。接收一个 Collector接口的实现 用于给Stream中元素做汇总的方法 |
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例。
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestEndStream {
@Test
public void test01() {
Stream<Integer> stream = Stream.of(11, 93, 50, 72, 9);
// 判断是否都满足 偶数的要求
boolean result = stream.allMatch(t -> t % 2 == 0);
System.out.println(result);
}
@Test
public void test02() {
Stream<Integer> stream = Stream.of(11, 93, 50, 72, 9);
// 判断是否有数字满足 偶数的要求
boolean result = stream.anyMatch(t -> t % 2 == 0);
System.out.println(result);
}
@Test
public void test03() {
Stream<Integer> stream = Stream.of(11, 93, 50, 72, 9);
// 判断是否都不满足 偶数的要求
boolean result = stream.noneMatch(t -> t % 2 == 0);
System.out.println(result);
}
@Test
public void test04() {
Stream<Integer> stream = Stream.of(11, 93, 50, 72, 9);
// 筛选出所有的偶数
stream = stream.filter(t -> t % 2 == 0);
// 获取流中的第一个元素
Optional<Integer> first = stream.findFirst();
System.out.println(first);
}
@Test
public void test05() {
Stream<Integer> stream = Stream.of(11, 93, 50, 72, 9);
// 统计流中元素的个数
long count = stream.count();
System.out.println(count);
}
@Test
public void test06() {
Stream<Integer> stream = Stream.of(11, 93, 50, 72, 9);
// 找出流中的最大值
Optional<Integer> max = stream.max((t1, t2) -> Integer.compare(t1, t2));
System.out.println(max);
}
@Test
public void test07() {
Stream<Integer> stream = Stream.of(11, 93, 50, 72, 9);
// 遍历流中的数据
stream.forEach(t -> System.out.println(t));
}
@Test
public void test08() {
Stream<Integer> stream = Stream.of(11, 93, 50, 72, 9);
// 使用reduce方法将流中的元素累加
Optional<Integer> sum = stream.reduce((i1, i2) -> i1 + i2);
System.out.println(sum);
}
@Test
public void test09() {
Stream<Integer> stream = Stream.of(11, 93, 50, 72, 9);
// 筛选出所有偶数 放入集合中
List<Integer> list = stream.filter(t -> t % 2 == 0)
.collect(Collectors.toList());
System.out.println(list);
}
}
方法引用与构造器引用
Lambda表达式是可以简化函数式接口的变量与形参赋值的语法。而方法引用和构造器引用是为了简化Lambda表达式的。
当Lambda表达式满足一些特殊的情况时,还可以再简化:
-
Lambda体只有一句语句,并且是通过调用一个对象/类的方法来完成的
-
Lambda表达式的形参正好全部用上,Lambda体中没有额外的数据参与
序号 | 语法格式 | 场景 |
---|---|---|
1 | 实例对象名::实例方法 | Lambda表达式有多个形参,Lambda体是调用Lambda体外的某个实例对象的实例方法完成,并且Lambda表达式的形参正好依次按顺序作为该方法调用的实参 |
2 | 类名::静态方法 | Lambda表达式有多个形参,Lambda体是调用某个类的静态方法完成,并且Lambda表达式的形参正好依次按顺序作为该方法调用的实参 |
3 | 类名::实例方法 | Lambda表达式只有1个形参,该参数正好是Lambda体中调用方法的对象 |
Lambda表达式有多个形参,其中第1个参数正好是Lambda体中调用方法的对象,其余形参正好依次按顺序作为该方法调用的实参 | ||
4 | 类名::new | 当Lambda表达式是一个new表达式,并且Lambda表达式形参正好依次按顺序作为所调用构造器的实参 |
5 | 数组类型名::new | 当Lambda表达式是一个创建数组对象的new表达式,Lambda表达式的形参正好是创建数组的长度 |
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestMethodReference {
@Test
public void test01() {
List<Integer> list = Arrays.asList(1, 3, 4, 8, 9);
// list.forEach(t -> System.out.println(t));
// out 是PrintStream对象 符合情况1 用方法引用简化
list.forEach(System.out::println);
}
@Test
public void test02() {
// Stream<Double> stream = Stream.generate(() -> Math.random());
// Math的静态方法 符合情况2 引用简化
Stream<Double> stream = Stream.generate(Math::random)
.limit(10);
stream.forEach(System.out::println);
}
@Test
public void test03() {
String[] arr = {"hello", "java", "world", "ok", "yes"};
// Arrays.sort(arr, ((o1, o2) -> o1.length() - o2.length()));
// Arrays.sort(arr, Comparator.comparing(s -> s.length()));
// 符合情况3(1)
Arrays.sort(arr, Comparator.comparing(String::length));
// Arrays.sort(arr, (s1, s2) -> s1.compareTo(s2));
// 符合情况3(2)
Arrays.sort(arr, String::compareTo);
}
@Test
public void test04() {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"3.14","9.8324","0.23427123");
// 使用数组中的元素创建BigDecimal对象
/*List<BigDecimal> result = list.stream()
.map(num -> new BigDecimal(num))
.collect(Collectors.toList());*/
List<BigDecimal> result = list.stream()
.map(BigDecimal::new)
.collect(Collectors.toList());
result.forEach(System.out::println);
}
@Test
public void test05() {
Optional<Integer> opt1 = Optional.ofNullable(16);
// Optional<String[]> opt2 = opt1.map(len -> new String[len]);
Optional<String[]> opt2 = opt1.map(String[]::new);
System.out.println(opt2.orElse(new String[0]).length);
}
}
Optional类
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样就不用显式进行空值检测。
序号 | 构造器或方法 | 描述 |
---|---|---|
1 | static Optional empty() | 用来创建一个空的Optional |
2 | static Optional of(T value) | 用来创建一个非空的Optional |
3 | static Optional ofNullable(T value) | 用来创建一个可能是空,也可能非空的Optional |
4 | T get() | 返回Optional容器中的对象。要求Optional容器必须非空。T get()与of(T value)使用是安全的 |
5 | T orElse(T other) | 如果Optional容器中非空,就返回所包装值,如果为空,就用orElse(T other)other指定的默认值(备胎)代替。一般orElse(T other) 与ofNullable(T value)配合使用 |
6 | T orElseGet(Supplier<? extends T> other) | 如果Optional容器中非空,就返回所包装值,如果为空,就用Supplier接口的Lambda表达式提供的值代替 |
7 | T orElseThrow(Supplier<? extends X> exceptionSupplier) | 如果Optional容器中非空,就返回所包装值,如果为空,就抛出你指定的异常类型代替原来的NoSuchElementException |
8 | boolean isPresent() | 判断Optional容器中的值是否存在 |
9 | void ifPresent(Consumer<? super T> consumer) | 判断Optional容器中的值是否存在,如果存在,就对它进行Consumer指定的操作,如果不存在就不做 |
10 | Optional map(Function<? super T,? extends U> mapper) | 判断Optional容器中的值是否存在,如果存在,就对它进行Function接口指定的操作,如果不存在就不做 |
import java.util.Optional;
import org.junit.Test;
public class TestOptional {
@Test
public void test01(){
String str = "Hello";
Optional<String> opt = Optional.ofNullable(str);
//判断是否是纯字母单词,如果是,转为大写,否则保持不变
String result = opt.filter(s->s.matches("[a-zA-Z]+"))
.map(s -> s.toLowerCase()).orElse(str);
System.out.println(result);
}
@Test
public void test02(){
String str = null;
Optional<String> opt = Optional.ofNullable(str);
// 如果是null就抛出异常
String string = opt.orElseThrow(()->new RuntimeException("值不存在"));
System.out.println(string);
}
@Test
public void test03(){
String str = null;
// 如果是空就用Supplier提供的值代替
Optional<String> opt = Optional.ofNullable(str);
String string = opt.orElseGet(()->new String("atguigu"));
System.out.println(string);
}
@Test
public void test04(){
String str = null;
Optional<String> opt = Optional.ofNullable(str);
// System.out.println(opt.get()); //java.util.NoSuchElementException: No value present
String string = opt.orElse("hello");
System.out.println(string);
}
@Test
public void test05(){
String str = "hello";
Optional<String> opt = Optional.of(str); // 创建一个非空Optional对象
String string = opt.get();
System.out.println(string);
}
@Test
public void test06(){
String str = null;
Optional<String> opt = Optional.ofNullable(str);
System.out.println(opt);
}
@Test
public void test07(){
Optional<String> opt = Optional.empty(); // 创建一个空的Optional对象
System.out.println(opt);
}
}