前文回顾
文章目录
前言
Stream API是Java8中继 Lambda 表达式外的另一个最为重要的改变。
一、Stream API
🍎 Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提供Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
🍎 Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。 也可以使用 Stream API 来并行执行操作。简言之, Stream API 提供了一种高效且易于使用的处理数据的方式。
🍎 实际开发中,项目中多数数据源都来自于Mysql, Oracle等。但现在数据源可以更多了,有MongDB, Radis等,而这些NoSQL的数据就需要Java层面去处理。
🍎 Stream 和 Collection 集合的区别: Collection 是一种静态的内存数据结构,而 Stream 是有关计算的。 前者是主要面向内存,存储在内存中,后者主要是面向 CPU,通过 CPU 实现计算。
🍎 “集合讲的是数据, Stream讲的是计算!”
注意:
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
1.Stream 的操作三个步骤
⭐️ 1- 创建 Stream
一个数据源(如:集合、数组),获取一个流。
⭐️ 2- 中间操作
一个中间操作链,对数据源的数据进行处理。
⭐️ 3- 终止操作(终端操作)
一旦执行终止操作, 就执行中间操作链,并产生结果。之后,不会再被使用。
2.创建 Stream
提供测试所用数据,后续举例代码中会用
public class Employee {
private int id;
private String name;
private int age;
private double salary;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public Employee() {
System.out.println("Employee().....");
}
public Employee(int id) {
System.out.println("Employee(int id).....");
this.id = id;
}
public Employee(int id, String name) {
this.id = id;
this.name = name;
System.out.println("Employee(int id, String name).....");
}
public Employee(int id, String name, int age, double salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public String toString() {
return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}';
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Employee employee = (Employee) o;
if (id != employee.id)
return false;
if (age != employee.age)
return false;
if (Double.compare(employee.salary, salary) != 0)
return false;
return name != null ? name.equals(employee.name) : employee.name == null;
}
@Override
public int hashCode() {
int result;
long temp;
result = id;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
temp = Double.doubleToLongBits(salary);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
}
public class EmployeeData {
public static List<Employee> getEmployees(){
List<Employee> list = new ArrayList<>();
list.add(new Employee(1001, "马化腾", 34, 6000.38));
list.add(new Employee(1002, "马云", 12, 9876.12));
list.add(new Employee(1003, "刘强东", 33, 3000.82));
list.add(new Employee(1004, "雷军", 26, 7657.37));
list.add(new Employee(1005, "李彦宏", 65, 5555.32));
list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));
list.add(new Employee(1007, "任正非", 26, 4333.32));
list.add(new Employee(1008, "扎克伯格", 35, 2500.32));
return list;
}
}
方式一:通过集合
🍎 Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
方法 | 说明 |
---|---|
default Stream< E > stream() | 返回一个顺序流 |
default Stream< E > parallelStream() | 返回一个并行流 |
代码如下(示例):
public class StreamAPITest {
/*
创建 Stream方式一:通过集合
1. default Stream<E> stream() : 返回一个顺序流
2. default Stream<E> parallelStream() : 返回一个并行流
*/
@Test
public void test1() {
List<Employee> employees = EmployeeData.getEmployees();
// default Stream<E> stream() : 返回一个顺序流
Stream<Employee> stream = employees.stream();
// default Stream<E> parallelStream() : 返回一个并行流
Stream<Employee> parallelStream = employees.parallelStream();
}
}
方式二:通过数组
🍎 Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
方法 | 说明 |
---|---|
static < T > Stream< T > stream(T [ ] array) | 返回一个流 |
重载形式,能够处理对应基本类型的数组:
⭐️ public static IntStream stream(int[ ] array)
⭐️ public static LongStream stream(long[ ] array)
⭐️ public static DoubleStream stream(double[ ] array)
代码如下(示例):
public class StreamAPITest {
/*
创建 Stream方式二:通过数组
1. 调用Arrays类的 static <T> Stream<T> stream(T[] array): 返回一个流
*/
@Test
public void test2() {
// static <T> Stream<T> stream(T[] array): 返回一个流
int[] arr = new int[]{1, 2, 3};
IntStream stream = Arrays.stream(arr);
Employee e1 = new Employee(1001, "Tom");
Employee e2 = new Employee(1002, "Jerry");
Employee[] arr1 = new Employee[]{e1, e2};
Stream<Employee> stream1 = Arrays.stream(arr1);
}
}
方式三:通过Stream的of()
🍎 可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。
方法 | 说明 |
---|---|
public static< T > Stream< T > of(T… values) | 返回一个流 |
代码如下(示例):
public class StreamAPITest {
/*
创建 Stream方式三:通过Stream的of()
1. public static<T> Stream<T> of(T... values) : 返回一个流
*/
@Test
public void test3() {
// public static<T> Stream<T> of(T... values) : 返回一个流
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5, 6);
}
}
方式四:创建无限流
🍎 可以使用静态方法 Stream.iterate() 和 Stream.generate(),创建无限流。
方法 | 说明 |
---|---|
public static< T > Stream< T > iterate(final T seed, final UnaryOperator< T > f) | 迭代 |
public static< T > Stream< T > generate(Supplier< T > s) | 生成 |
代码如下(示例):
public class StreamAPITest {
/*
创建 Stream方式四:创建无限流
1. 迭代
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
2. 生成
public static<T> Stream<T> generate(Supplier<T> s)
*/
@Test
public void test4() {
// 迭代 public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
// 遍历前10个偶数
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out :: println);
// 生成 public static<T> Stream<T> generate(Supplier<T> s)
// 创建10个随机数
Stream.generate(Math :: random).limit(10).forEach(System.out :: println);
}
}
3.Stream 的中间操作
多个 中间操作 可以连接起来形成一个 流水线 ,除非流水线上触发终止操作,否则 中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值” 。
筛选与切片
方法 | 说明 |
---|---|
filter(Predicate p) | 接收 Lambda , 从流中排除某些元素 |
distinct() | 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 |
limit(long maxSize) | 截断流,使其元素不超过给定数量 |
skip(long n) | 跳过元素,返回一个扔掉了前n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 |
代码如下(示例):
public class StreamAPITest1 {
/*
1、筛选与切片
filter(Predicate p) 接收 Lambda , 从流中排除某些元素
distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize) 截断流,使其元素不超过给定数量
skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
*/
@Test
public void test1() {
// filter(Predicate p) 接收 Lambda , 从流中排除某些元素
List<Employee> employeeList = EmployeeData.getEmployees();
Stream<Employee> stream = employeeList.stream();
stream.filter(e -> e.getSalary() > 7000).forEach(System.out :: println); // 查询工资大于7000的员工信息
System.out.println();
// limit(long maxSize) 截断流,使其元素不超过给定数量
employeeList.stream().limit(3).forEach(System.out :: println);
System.out.println();
// skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
employeeList.stream().skip(3).forEach(System.out :: println);
System.out.println();
// distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
employeeList.add(new Employee(1010, "刘强东", 40, 8000));
employeeList.add(new Employee(1010, "刘强东", 55, 8000));
employeeList.add(new Employee(1010, "刘强东", 40, 8000));
employeeList.add(new Employee(1010, "刘强东", 40, 8000));
employeeList.stream().distinct().forEach(System.out :: println);
}
}
映 射
方法 | 说明 |
---|---|
map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 |
mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream |
mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream |
mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream |
flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流 |
代码如下(示例):
public class StreamAPITest1 {
/*
2、映射
map(Function f) 接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
*/
@Test
public void test2() {
// map(Function f) 接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素
List<String> stringList = Arrays.asList("aa", "bb", "cc", "dd");
stringList.stream().map(str -> str.toUpperCase()).forEach(System.out :: println);
System.out.println();
// 练习1:获取员工姓名长度大于3的员工的姓名
List<Employee> employeeList = EmployeeData.getEmployees();
// Stream<String> nameStream = employeeList.stream().map(e -> e.getName());
Stream<String> nameStream = employeeList.stream().map(Employee::getName);
nameStream.filter(name -> name.length() > 3).forEach(System.out :: println);
System.out.println();
// 练习2:
Stream<Stream<Character>> streamStream = stringList.stream().map(StreamAPITest1::fromStringToStream);
streamStream.forEach(streamChar -> {
streamChar.forEach(System.out :: println);
});
System.out.println();
// flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
Stream<Character> characterStream = stringList.stream().flatMap(StreamAPITest1::fromStringToStream);
characterStream.forEach(System.out :: println);
}
// 将字符串中的多个字符构成的集合转换为对应的 Stream 的实例
public static Stream<Character> fromStringToStream(String str) {
ArrayList<Character> list = new ArrayList<>();
for (Character c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
@Test
public void test3() {
ArrayList list1 = new ArrayList();
list1.add(1);
list1.add(2);
list1.add(3);
ArrayList list2 = new ArrayList();
list2.add(4);
list2.add(5);
list2.add(6);
// list1.add(list2);
// System.out.println(list1); // [1, 2, 3, [4, 5, 6]]
list1.addAll(list2);
System.out.println(list1); // [1, 2, 3, 4, 5, 6]
}
}
排序
方法 | 说明 |
---|---|
sorted() | 产生一个新流,其中按自然顺序排序 |
sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 |
代码如下(示例):
public class StreamAPITest1 {
/*
3、排序
sorted() 产生一个新流,其中按自然顺序排序
sorted(Comparator com) 产生一个新流,其中按比较器顺序排序
*/
@Test
public void test4() {
// sorted() 产生一个新流,其中按自然顺序排序
List<Integer> list = Arrays.asList(12, 58, 0, -10);
list.stream().sorted().forEach(System.out :: println);
// 抛异常。原因:Employee没有实现Comparable接口
// List<Employee> employees = EmployeeData.getEmployees();
// employees.stream().sorted().forEach(System.out :: println);
System.out.println();
// sorted(Comparator com) 产生一个新流,其中按比较器顺序排序
List<Employee> employees = EmployeeData.getEmployees();
employees.stream().sorted( (e1, e2) -> {
int ageValue = Integer.compare(e1.getAge(), e2.getAge());
if (ageValue != 0) {
return ageValue;
} else {
return -Double.compare(e1.getAge(), e2.getAge());
}
} ).forEach(System.out :: println);
}
}
4.Stream 的终止操作
🍎 终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如: List、 Integer,甚至是 void 。
🍎 流进行了终止操作后,不能再次使用。
匹配与查找
方法 | 说明 |
---|---|
allMatch(Predicate p) | 检查是否匹配所有元素 |
anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
findFirst() | 返回第一个元素 |
findAny() | 返回当前流中的任意元素 |
count() | 返回流中元素总数 |
max(Comparator c) | 返回流中最大值 |
min(Comparator c) | 返回流中最小值 |
forEach(Consumer c) | 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反, Stream API 使用内部迭代——它帮你把迭代做了) |
代码如下(示例):
public class StreamAPITest2 {
/*
1、匹配与查找
allMatch(Predicate p) 检查是否匹配所有元素
练习:是否所有的员工的年龄都大于18
anyMatch(Predicate p) 检查是否至少匹配一个元素
练习:是否存在员工的工资大于10000
noneMatch(Predicate p) 检查是否没有匹配所有元素
练习:是否存在员工姓“雷”
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素
count() 返回流中元素总数
max(Comparator c) 返回流中最大值
练习:返回最高的工资
min(Comparator c) 返回流中最小值
练习:返回最低工资的员工
forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。
相反,Stream API 使用内部迭代——它帮你把迭代做了)
*/
@Test
public void test1() {
List<Employee> employees = EmployeeData.getEmployees();
// allMatch(Predicate p) 检查是否匹配所有元素
// 练习:是否所有的员工的年龄都大于18
boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println(allMatch);
// anyMatch(Predicate p) 检查是否至少匹配一个元素
// 练习:是否存在员工的工资大于10000
boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000);
System.out.println(allMatch);
// noneMatch(Predicate p) 检查是否没有匹配所有元素
// 练习:是否存在员工姓“雷”
boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷"));
System.out.println(noneMatch);
// findFirst() 返回第一个元素
Optional<Employee> employee = employees.stream().findFirst();
System.out.println(employee);
// findAny() 返回当前流中的任意元素
Optional<Employee> employee1 = employees.parallelStream().findAny();
System.out.println(employee1);
}
@Test
public void test2() {
List<Employee> employees = EmployeeData.getEmployees();
// count() 返回流中元素总数
long count = employees.stream().filter(e -> e.getSalary() > 5000).count();
System.out.println(count);
// max(Comparator c) 返回流中最大值
// 练习:返回最高的工资
Stream<Double> doubleStream = employees.stream().map(e -> e.getSalary());
Optional<Double> max = doubleStream.max(Double::compare);
System.out.println(max);
// min(Comparator c) 返回流中最小值
// 练习:返回最低工资的员工
Optional<Employee> min = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(min);
System.out.println();
// forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。
// 相反,Stream API 使用内部迭代——它帮你把迭代做了)
employees.stream().forEach(System.out :: println);
// 使用集合的遍历操作
employees.forEach(System.out :: println);
}
}
归约
方法 | 说明 |
---|---|
reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 T |
reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回Optional< T > |
代码如下(示例):
public class StreamAPITest2 {
/*
2、归约
reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
练习:计算1——10的自然数的和
reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
练习:计算公司所有员工的工资之和
*/
@Test
public void test3() {
// reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
// 练习:计算1——10的自然数的和
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Integer reduce = list.stream().reduce(0, Integer::sum);
System.out.println(reduce);
// reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
// 练习:计算公司所有员工的工资之和
List<Employee> employees = EmployeeData.getEmployees();
// Stream<Double> salaryStream = employees.stream().map(e -> e.getSalary());
Stream<Double> salaryStream = employees.stream().map(Employee :: getSalary);
Optional<Double> sumSalary = salaryStream.reduce(Double::sum);
// System.out.println(sumSalary);
System.out.println(sumSalary.get());
}
}
收集
方法 | 说明 |
---|---|
collect(Collector c) | 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法 |
代码如下(示例):
public class StreamAPITest2 {
/*
3、收集
collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
*/
@Test
public void test4() {
// collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
// 练习:查询工资大于6000的员工,结果返回为一个List或Set
List<Employee> employees = EmployeeData.getEmployees();
List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList());
employeeList.forEach(System.out :: println);
System.out.println();
Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet());
employeeSet.forEach(System.out :: println);
}
}
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、 Set、Map)。
Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:
二、Optional类
🍎 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发, Optional类已经成为Java 8类库的一部分。
🍎 Optional 类(java.util.Optional) 是一个容器类, 它可以保存类型T的值, 代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
🍎 Optional类的Javadoc描述如下:这是一个可以为null的容器对象。如果值存在则isPresent() 方法会返回true,调用 get() 方法会返回该对象。
🍎 Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
⭐️ 创建Optional类对象的方法:
- Optional.of(T t) : 创建一个 Optional 实例, t 必须非空;
- Optional.empty() : 创建一个空的 Optional 实例
- Optional.ofNullable(T t): t 可以为null
⭐️ 判断Optional容器中是否包含对象:
- boolean isPresent() : 判断是否包含对象
- void ifPresent(Consumer<? super T> consumer) : 如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。
⭐️ 获取Optional容器的对象:
- T get(): 如果调用对象包含值,返回该值,否则抛异常
- T orElse(T other) : 如果有值则将其返回,否则返回指定的other对象。
- T orElseGet(Supplier<? extends T> other) : 如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
- T orElseThrow(Supplier<? extends X> exceptionSupplier) : 如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。
提供测试所用数据,举例代码中会用
public class Girl {
private String name;
public Girl() {
}
public Girl(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Girl{" +
"name='" + name + '\'' +
'}';
}
}
public class Boy {
private Girl girl;
public Boy() {
}
public Boy(Girl girl) {
this.girl = girl;
}
public Girl getGirl() {
return girl;
}
public void setGirl(Girl girl) {
this.girl = girl;
}
@Override
public String toString() {
return "Boy{" +
"girl=" + girl +
'}';
}
}
代码如下(示例):
/**
* Optional 类:为了在程序中避免出现空指针异常而创建的。
* 常用的方法:
* ofNullable(T t)
* orElse(T t)
*/
public class OptionalTest {
/*
1. Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
2. Optional.empty() : 创建一个空的 Optional 实例
3. Optional.ofNullable(T t):创建一个 Optional 实例,t可以为 null
*/
@Test
public void test1() {
Girl girl = new Girl();
// girl = null;
// of(T t):必须保证 t 非空
Optional<Girl> optionalGirl = Optional.of(girl);
}
@Test
public void test2() {
Girl girl = new Girl();
girl = null;
// ofNullable(T t):t可以为null
Optional<Girl> optionalGirl = Optional.ofNullable(girl);
System.out.println(optionalGirl); // Optional.empty 一个空的 Optional 实例
// orElse(T t1): 如果当前的 Optional 内部封装的 t 是非空的,则返回内部的 t,
// 如果内部的 t 是空的,则返回 orElse()方法中的参数 t1。
Girl girl1 = optionalGirl.orElse(new Girl("杨幂"));
System.out.println(girl1);
}
public String getGirlName(Boy boy) {
return boy.getGirl().getName();
}
@Test
public void test3() {
Boy boy = new Boy();
String girlName = getGirlName(boy);
System.out.println(girlName); // NullPointerException
}
// 优化以后的 getGirlName()
public String getGirlName1(Boy boy) {
if (boy != null) {
Girl girl = boy.getGirl();
if (girl != null) {
girl.getName();
}
}
return null;
}
@Test
public void test4() {
Boy boy = new Boy();
String girlName = getGirlName1(boy);
System.out.println(girlName); // null
}
// 使用 Optional 类的 getGirlName()
public String getGirlName2(Boy boy) {
Optional<Boy> optionalBoy = Optional.ofNullable(boy);
// 此时的 boy1 一定非空
Boy boy1 = optionalBoy.orElse(new Boy(new Girl("苍老师")));
Girl girl = boy1.getGirl();
Optional<Girl> optionalGirl = Optional.ofNullable(girl);
// 此时的 girl1 一定非空
Girl girl1 = optionalGirl.orElse(new Girl("古力娜扎"));
return girl1.getName();
}
@Test
public void test5() {
Boy boy = new Boy(); // 古力娜扎
boy = null; // 苍老师
boy = new Boy(new Girl("迪丽热巴")); // 迪丽热巴
String girlName2 = getGirlName2(boy);
System.out.println(girlName2);
}
}