1、Lambda 表达式
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
使用 Lambda 表达式可以使代码变的更加简洁紧凑。
-
可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
-
可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
-
可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
-
可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。
1.2 Lambda 表达式实例
Lambda 表达式的简单例子
public class Java8Tester { public static void main(String args[]){ Java8Tester tester = new Java8Tester(); // 类型声明 MathOperation addition = (int a, int b) -> a + b; // 不用类型声明 MathOperation subtraction = (a, b) -> a - b; // 大括号中的返回语句 MathOperation multiplication = (int a, int b) -> { return a * b; }; // 没有大括号及返回语句 MathOperation division = (int a, int b) -> a / b; System.out.println("10 + 5 = " + tester.operate(10, 5, addition)); System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction)); System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication)); System.out.println("10 / 5 = " + tester.operate(10, 5, division)); // 不用括号 GreetingService greetService1 = message -> System.out.println("Hello " + message); // 用括号 GreetingService greetService2 = (message) -> System.out.println("Hello " + message); greetService1.sayMessage("Runoob"); greetService2.sayMessage("Google"); } interface MathOperation { int operation(int a, int b); } interface GreetingService { void sayMessage(String message); } private int operate(int a, int b, MathOperation mathOperation){ return mathOperation.operation(a, b); } }
2、方法引用
方法引用通过方法的名字来指向一个方法。
方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
方法引用使用一对冒号 :: 。
2.1 使用场景
我们用Lambda表达式来实现匿名方法。但有些情况下,我们用Lambda表达式仅仅是调用一些已经存在的方法,除了调用动作外,没有其他任何多余的动作,
在这种情况下,我们倾向于通过方法名来调用它,而Lambda表达式可以帮助我们实现这一要求,它使得Lambda在调用那些已经拥有方法名的方法的代码更简洁、更容易理解。
方法引用可以理解为Lambda表达式的另外一种表现形式。
2.2 方法引用的分类
类型 | 语法 | 对应的Lambda表达式 |
---|---|---|
静态方法引用 | 类名::staticMethod | (args) -> 类名.staticMethod(args) |
实例方法引用 | inst::instMethod | (args) -> inst.instMethod(args) |
对象方法引用 | 类名::instMethod | (inst,args) -> 类名.instMethod(args) |
构建方法引用 | 类名::new | (args) -> new 类名(args) |
2.3 方法引用举例
2.3.1 静态方法引用
现假设,一个部门有5人,把他们存放在一个集合中,并按年龄排序
import lombok.AllArgsConstructor; import lombok.Data; import java.util.*; public class Test { public static void main(String[] args) { List<Person> personList1 = new ArrayList<Person>(5); List<Person> personList2 = new ArrayList<Person>(5); List<Person> personList3 = new ArrayList<Person>(5); initData(personList1); // 初始化数据 initData(personList2); initData(personList3); // 自定义比较器 Collections.sort(personList1, new Comparator<Person>() { public int compare(Person o1, Person o2) { /* 正序排序 */ return o1.getAge() - o2.getAge(); } }); System.out.println("自定义比较器: " + personList1); // Lambda表达式:Person类中已经有了一个静态方法的比较器:compareByAge Collections.sort(personList2, (a,b) -> Person.compareByAge(a,b)); System.out.println("Lambda表达式: " + personList2); // 方法引用 Collections.sort(personList3, Person::compareByAge); System.out.println("方法引用: " + personList3); } private static void initData(List<Person> rosterAsArray){ Random r = new Random(); for (int i = 1; i <= 5; i++) { rosterAsArray.add(new Person("小" + i, r.nextInt(100))); } } } @Data @AllArgsConstructor class Person { private String name; private Integer age; // 定义一个比较器 public static int compareByAge(Person a, Person b) { return a.age.compareTo(b.age); } }
输出结果:
自定义比较器: [Person(name=小5, age=5), Person(name=小1, age=56), Person(name=小2, age=73), Person(name=小4, age=73), Person(name=小3, age=88)] Lambda表达式: [Person(name=小5, age=9), Person(name=小3, age=67), Person(name=小4, age=73), Person(name=小2, age=84), Person(name=小1, age=94)] 方法引用: [Person(name=小3, age=43), Person(name=小5, age=59), Person(name=小4, age=79), Person(name=小1, age=92), Person(name=小2, age=96)]
2.3.2 实例方法引用
实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。
import lombok.AllArgsConstructor; import lombok.Data; import java.util.function.Supplier; public class Test { public static void main(String[] args) { Person person = new Person("张国荣",18); /** * 1.supplier是个接口,有一个get()方法 * 2.每次调用get()方法时都会调用构造方法创建一个新对象 * */ Supplier<String> supplier1 = () -> person.getName(); System.out.println("Lambda表达式输出结果:" + supplier1.get()); Supplier<String> supplier2 = person::getName; System.out.println("方法引用输出结果:" + supplier2.get()); } } @Data @AllArgsConstructor class Person { private String name; private Integer age; }
输出结果:
Lambda表达式输出结果:欧阳峰
实例方法引用输出结果:欧阳峰
2.3.3 对象方法引用
若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。
public class Test { public static void main(String[] args) { /** BiPredicate是一个函数接口,它接受两个参数并返回一个布尔值 */ BiPredicate<String,String> bp = (x, y) -> x.equals(y); BiPredicate<String,String> bp1 = String::equals; boolean test = bp1.test("xy", "xx"); System.out.println(test); } }
BiPredicate的test()方法接受两个参数,x和y,具体实现为x.equals(y),满足Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数,因此可以使用对象方法引用。
2.3.4 构造方法引用
注意:
需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致。
public class Test { public static void main(String[] args) { Supplier<List<Person>> personSupplier1 = () -> new ArrayList<>(); List<Person> person1 = personSupplier1.get(); Supplier<List<Person>> personSupplier2 = ArrayList<Person>::new; // 构造方法引用写法 List<Person> person2 = personSupplier2.get(); } }
3、 默认方法
3.1 为什么要有默认方法
在 java 8 之前,接口与其实现类之间的 耦合度 太高了(tightly coupled),当需要为一个接口添加方法时,所有的实现类都必须随之修改。
默认方法解决了这个问题,它可以为接口添加新的方法,而不会破坏已有的接口的实现。
3.2 一个简单的例子
public class Test { public static void main(String[] args) {} } interface Person { void getName(); void getAge(); // 后面需求,添加一个职位(但是有些人有职位,有些人目前无业),不更改之前代码 default void getJob() { System.out.println("你的职位是啥?"); } } class 张三 implements Person { @Override public void getName() {} @Override public void getAge() {} @Override public void getJob() { System.out.println("法外狂徒!!!"); } } class 李四 implements Person { @Override public void getName() {} @Override public void getAge() {} // 无业游民 }
接口默认方法的继承分三种情况:
不覆写默认方法,直接从父接口中获取方法的默认实现。
覆写默认方法,这跟类与类之间的覆写规则相类似。
覆写默认方法并将它重新声明为抽象方法,这样新接口的子类必须再次覆写并实现这个抽象方法。
3.3 其他注意点
-
default
关键字只能在接口中使用(以及用在switch
语句的default
分支),不能用在抽象类中。 -
接口默认方法不能覆写
Object
类的equals
、hashCode
和toString
方法。 -
接口中的静态方法必须是
public
的,public
修饰符可以省略,static
修饰符不能省略。 -
即使使用了 java 8 的环境,一些 IDE 仍然可能在一些代码的实时编译提示时出现异常的提示(例如无法发现 java 8 的语法错误),因此不要过度依赖 IDE。
4、新工具
新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps 。
从 JDK 1.8 开始,Nashorn取代Rhino(JDK 1.6, JDK1.7) 成为 Java 的嵌入式 JavaScript 引擎。Nashorn 完全支持 ECMAScript 5.1 规范以及一些扩展。
它使用基于 JSR 292 的新语言特性,其中包含在 JDK 7 中引入的 invokedynamic,将 JavaScript 编译成 Java 字节码。 与先前的 Rhino 实现相比,这带来了 2 到 10倍的性能提升。
4.1 jjs
jjs是个基于Nashorn引擎的命令行工具。它接受一些JavaScript源代码为参数,并且执行这些源代码。
例如,我们创建一个具有如下内容的sample.js文件:
print('Hello World!');
打开控制台,输入以下命令:
D:UserDesktop>jjs sample.js
Hello World!
4.2 jjs 交互式编程
打开控制台,输入以下命令:
D:UserDesktop>jjs jjs> print("Hello, World!") Hello, World! jjs> quit() D:UserDesktop>
4.3 传递参数
打开控制台,输入以下命令:
D:UserDesktop>jjs -- a b c jjs> print('字母: ' +arguments.join(", ")) 字母: a, b, c jjs>
5、Stream API
Stream 是Java8中处理集合的关键抽象概念,它可以对集合进行非常复杂的查找、过滤、筛选等操作,在新版的JPA中,也已经加入了Stream。
5.1 Stream的操作步骤
Stream有如下三个操作步骤:
-
创建Stream
从一个数据源,如集合、数组中获取流。
-
中间操作
一个操作的中间链,对数据源的数据进行操作。
-
终止操作
一个终止操作,执行中间操作链,并产生结果。
要注意的是,对流的操作完成后需要进行关闭操作(或者用JAVA7的try-with-resources)。
5.2 生成流
在 Java 8 中, 集合接口有两个方法来生成流:
stream()
− 为集合创建串行流。
parallelStream()
− 为集合创建并行流。
举例:
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
5.3 Stream中间操作--筛选与切片
filter
:接收Lambda,从流中排除某些操作;
limit
:截断流,使其元素不超过给定对象
skip(n)
:跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
distinct
:筛选,通过流所生成元素的hashCode()和equals()去除重复元素。
举例:
import lombok.AllArgsConstructor; import lombok.Data; import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { List<Person> personList = new ArrayList<>(); initData(personList); // 初始化数据 /** Stream中间操作--筛选与切片 */ // 1.filter(过滤20岁以下) personList.stream().filter(a -> a.getAge() < 20).forEach(System.out::print); /* 结果:Person(name=欧阳雪, age=18, country=中国, sex=F) */ // 2.limit(从Person列表中取出两个女性) personList.stream().filter((p) -> p.getSex() == 'F').limit(2).forEach(System.out::println); /* Person(name=欧阳雪, age=18, country=中国, sex=F) * Person(name=Harley, age=22, country=英国, sex=F) * */ // 3.skip(从Person列表中从第2个女性开始,取出所有的女性) personList.stream().filter((p) -> p.getSex() == 'F').skip(1).forEach(System.out::println); /* Person(name=Harley, age=22, country=英国, sex=F) * Person(name=小梅, age=20, country=中国, sex=F) * Person(name=何雪, age=21, country=中国, sex=F) * */ // 4.distinct(男性中有两个李康,去除掉了一个重复的) personList.stream().filter((p) -> p.getSex() == 'M').distinct().forEach(System.out::println); } private static void initData(List<Person> personList) { personList.add(new Person("欧阳雪",18,"中国",'F')); personList.add(new Person("Tom",24,"美国",'M')); personList.add(new Person("Harley",22,"英国",'F')); personList.add(new Person("向天笑",20,"中国",'M')); personList.add(new Person("李康",22,"中国",'M')); personList.add(new Person("小梅",20,"中国",'F')); personList.add(new Person("何雪",21,"中国",'F')); personList.add(new Person("李康",22,"中国",'M')); } } @Data @AllArgsConstructor class Person { private String name; private Integer age; private String country; private char sex; }
5.4 Stream中间操作--映射
map
--接收Lambda,将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap
--接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
举例:
map
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.ArrayList; import java.util.List; import java.util.function.BiConsumer; public class Test { public static void main(String[] args) { List<Person> personList = new ArrayList<>(); initData(personList); // 初始化数据 /** Stream中间操作--映射 */ // map(把所有人的国家单独提取) // 使用Stream实例化新对象:2种方法 // 可以添加构造函数 personList.stream().map(a -> new PersonCountry(a.getCountry())).distinct().forEach(System.out::println); System.out.println("=========================="); // 不能添加构造函数 personList.stream().map((p) -> { PersonCountry personName = new PersonCountry(); personName.setCountry(p.getCountry()); return personName; }).distinct().forEach(System.out::println); } private static void initData(List<Person> personList) { personList.add(new Person("欧阳雪",18,"中国",'F')); personList.add(new Person("Tom",24,"美国",'M')); personList.add(new Person("Harley",22,"英国",'F')); personList.add(new Person("向天笑",20,"中国",'M')); personList.add(new Person("李康",22,"中国",'M')); personList.add(new Person("小梅",20,"中国",'F')); personList.add(new Person("何雪",21,"中国",'F')); personList.add(new Person("李康",22,"中国",'M')); } } @Data @AllArgsConstructor @NoArgsConstructor class PersonCountry { private String country; } @Data @AllArgsConstructor class Person { private String name; private Integer age; private String country; private char sex; }
flatMap
flatMap把inputStream中的层级结构 扁平化,就是将最底层元素抽出来放到一起,最终output的新Stream里面已经没有List了,都是直接的数字。
import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream public class Test { public static void main(String[] args) { /** Stream中间操作--映射 */ // flatMap Stream<List<Integer>> inputStream = Stream.of( Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6) ); inputStream.flatMap((childList) -> childList.stream()).forEach(System.out::print); // 结果: 123456 System.out.println(); List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","ddd"); Stream<Character> streamStream = list.stream().flatMap(Test::getCharacterByString); streamStream.forEach(System.out::print); // 结果: aaabbbcccdddddd } public static Stream<Character> getCharacterByString(String str) { List<Character> characterList = new ArrayList<>(); for (Character character : str.toCharArray()) { characterList.add(character); } return characterList.stream(); } }
5.5 Stream中间操作--排序
sorted()
--自然排序(Comparable)
sorted(Comparator com)
--定制排序(Comparator)
import lombok.AllArgsConstructor; import lombok.Data; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Stream; public class Test { public static void main(String[] args) { List<Person> personList = new ArrayList<>(); initData(personList); /** Stream中间操作--排序 */ // sorted()--自然排序(Comparable) Arrays.asList(2, 6, 8, 9, 23, 4, 87, 5).stream().sorted().forEach(System.out::print); System.out.println("========================================="); // sorted(Comparator com)--定制排序(Comparator) personList.stream().sorted((p1, p2) -> { if (p1.getAge().equals(p2.getAge())) { return p1.getName().compareTo(p2.getName()); } else { return p1.getAge().compareTo(p2.getAge()); } }).forEach(System.out::println); } public static void initData(List<Person> personList) { personList.add(new Person("欧阳雪", 18, "中国", 'F')); personList.add(new Person("Tom", 24, "美国", 'M')); personList.add(new Person("Harley", 22, "英国", 'F')); personList.add(new Person("向天笑", 20, "中国", 'M')); personList.add(new Person("李康", 22, "中国", 'M')); personList.add(new Person("小梅", 20, "中国", 'F')); personList.add(new Person("何雪", 21, "中国", 'F')); personList.add(new Person("李康", 22, "中国", 'M')); } } @Data @AllArgsConstructor class Person { private String name; private Integer age; private String country; private char sex; }
5.6 终止操作--查找与匹配
allMatch--检查是否匹配所有元素
anyMatch--检查是否至少匹配一个元素
noneMatch--检查是否没有匹配所有元素
findFirst--返回第一个元素
findAny--返回当前流中的任意元素
count--返回流中元素的总个数
max--返回流中最大值
min--返回流中最小值
举例:
import lombok.AllArgsConstructor; import lombok.Data; import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { List<Person> personList = new ArrayList<>(); initData(personList); /** 终止操作--查找与匹配 */ // allMatch boolean isChinese = personList.stream().allMatch(p -> p.getCountry().equals("中国")); System.out.println("是否都是中国人:" + isChinese); System.out.println("========================================="); // count long count = personList.stream().filter(a -> a.getAge() > 20).count(); System.out.println("大于20岁的人有:" + count); System.out.println("========================================="); // max min Person max = personList.stream().max((p1, p2) -> p1.getAge().compareTo(p2.getAge())).get(); System.out.println("年龄最大的人信息:" + max); Person min = personList.stream().min((p1, p2) -> p1.getAge().compareTo(p2.getAge())).get(); System.out.println("年龄最小的人信息:" + min); } public static void initData(List<Person> personList) { personList.add(new Person("欧阳雪", 18, "中国", 'F')); personList.add(new Person("Tom", 24, "美国", 'M')); personList.add(new Person("Harley", 22, "英国", 'F')); personList.add(new Person("向天笑", 20, "中国", 'M')); personList.add(new Person("李康", 22, "中国", 'M')); personList.add(new Person("小梅", 20, "中国", 'F')); personList.add(new Person("何雪", 21, "中国", 'F')); personList.add(new Person("李康", 22, "中国", 'M')); } } @Data @AllArgsConstructor class Person { private String name; private Integer age; private String country; private char sex; }
5.7 归约
reduce(T identity, BinaryOperator) / reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。
import java.util.ArrayList; import java.util.List; import java.util.Objects; public class Test { public static void main(String[] args) { /** 归约 */ List<Integer> integerList = new ArrayList<>(100); for (int i = 1; i <= 100; i++) { integerList.add(i); } Integer reduce = 0; // 求和 reduce = integerList.stream().reduce(Integer::sum).get(); System.out.println("总和结果为:" + reduce); // 含有初始标识的,求和 reduce = integerList.stream().reduce(0, (x, y) -> x + y); System.out.println("总和结果为:" + reduce); //对元素长度进行求和。 reduce = integerList.stream().map(Objects::toString).mapToInt(String::length).sum(); System.out.println("元素长度为:" + reduce); } }
5.8 收集
collect——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法。
import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class Test { public static void main(String[] args) { /** 收集聚合 */ List<Integer> hereList = Arrays.asList(15, 32, 5, 232, 56, 29, 104); //总和、平均值,最大值,最小值 int sum = hereList.stream().collect(Collectors.summingInt(Integer::intValue)); Double ave = hereList.stream().collect(Collectors.averagingInt(Integer::intValue)); Integer max = hereList.stream().collect(Collectors.maxBy(Integer::compare)).get(); Integer min = hereList.stream().collect(Collectors.minBy(Integer::compare)).get(); System.out.println("sum:" + sum + " " + "ave:" + ave + " " + "max:" + max + " " + "min:" + min); } }
6、Date Time API
Java 8通过发布新的Date-Time API (JSR 310)来进一步加强对日期与时间的处理。
在旧版的 Java 中,日期时间 API 存在诸多问题,其中有:
-
非线程安全 − java.util.Date 是非线程安全的,所有的日期类都是可变的,这是Java日期类最大的问题之一。
-
设计很差 − Java的日期/时间类的定义并不一致,在java.util和java.sql的包中都有日期类,此外用于格式化和解析的类在java.text包中定义。java.util.Date同时包含日期和时间,而java.sql.Date仅包含日期,将其纳入java.sql包并不合理。另外这两个类都有相同的名字,这本身就是一个非常糟糕的设计。
-
时区处理麻烦 − 日期类并不提供国际化,没有时区支持,因此Java引入了java.util.Calendar和java.util.TimeZone类,但他们同样存在上述所有的问题。
Java 8 在 java.time 包下提供了很多新的 API。以下为两个比较重要的 API:
-
Local(本地) − 简化了日期时间的处理,没有时区的问题。
-
Zoned(时区) − 通过制定的时区处理日期时间。
新的java.time包涵盖了所有处理日期,时间,日期/时间,时区,时刻(instants),过程(during)与时钟(clock)的操作。
全新的时间日期API移植到了规范的java.time包下(而不是原来的SimpleDateFormat还在java.text包下) 本地时间日期与时间戳 LocalDate、LocalTime、LocalDateTime——标准的ISO-8601标准,并且是线程安全的不可变对象!
public static void main(String[] args) { // LocalDate LocalTime LocalDateTime用法都是类似的 // now()——获取当前系统时间 LocalDateTime dateTime = LocalDateTime.now(); System.out.println("dateTime = " + dateTime); // dateTime = 2017-09-26T18:05:12.990 LocalDate date = LocalDate.now(); System.out.println("date = " + date); // date = 2017-09-26 // of()——根据指定时间参数返回对象 LocalDateTime of = LocalDateTime.of(2017, 10, 1, 10, 59); System.out.println("of = " + of); // of = 2017-10-01T10:59 LocalTime of1 = LocalTime.of(21, 58, 3); System.out.println("of1 = " + of1);// of1 = 21:58:03 // plus系列的时间偏移加,minus系列减 LocalDateTime dateTime1 = dateTime.plusYears(3); System.out.println("dateTime1 = " + dateTime1); // dateTime1 = 2020-09-26T18:12:20.622 LocalTime of2 = of1.minusHours(2); System.out.println("of2 = " + of2); // of2 = 19:58:03 // get系列获取时间日期字段 System.out.println(dateTime1.getYear()); // 2020 System.out.println(dateTime1.getDayOfWeek());// SATURDAY // 更多未演示方法,请参见API }
7、Optional 类
NullPointerException
相信每个JAVA程序员都不陌生,是JAVA应用程序中最常见的异常。之前,Google Guava项目曾提出用Optional类来包装对象从而解决NullPointerException。受此影响,JDK8的类中也引入了Optional类,在新版的SpringData Jpa和Spring Redis Data中都已实现了对该方法的支持。
7.1 Optional类
该方法的注释大致意思是:Optional是一个容器对象,它可能包含空值,也可能包含非空值。当属性value被设置时,isPesent()方法将返回true,并且get()方法将返回这个值。
该类支持泛型,即其属性value可以是任何对象的实例。
7.2 Optional类的方法
查看结构图可以看到有如下常用方法:
of(T)——创建一个非空的Optional实例(使用empty创建一个空的Optional实例)
ofNullable(T)——若参数不为Null,则创建一个非空实例,否则创建一个空实例
isPresent——是否存在值(存在返回true,否则返回false)
orElse(T)——有值则将其返回,否则返回参数中的指定值
get——有值则返回,否则抛出异常
orElseGet(Supplier)——与orElse类似,但他可以接收Supplier接口返回的值
map(Function)——有值则对其进行Function处理返回处理后的Optional(实际上之前两节已经接触过)
flatMap——与map类似,但是返回值必须是Optional
7.3 举例
import lombok.Data; import lombok.NoArgsConstructor; import org.apache.commons.lang3.StringUtils; import java.util.Optional; public class Test { public static void main(String[] args) { User user = new User(); System.out.println("Optional : " + getUserStreetName(user)); System.out.println("嵌套判断 : " + getUserStreetNameByIf(user)); } private static String getUserStreetName(User user) { Optional<User> userOptional = Optional.ofNullable(user); final String streetName = userOptional.orElse(new User()).getAddressOpt() .orElse(new Address()).getStreetOpt() .orElse(new Street()).getStreetName(); return StringUtils.isEmpty(streetName) ? "nothing found" : streetName; } private static String getUserStreetNameByIf(User user) { if (null != user) { Address address = user.getAddress(); if (null != address) { Street street = address.getStreet(); if (null != street) { return street.getStreetName(); } } } return "UNKNOWN"; } } @Data class User { private String name = "张三"; private Integer age = 18; private Address address; public Address getAddress() { return new Address(); } public Optional<Address> getAddressOpt() { return Optional.of(new Address()); } } @Data @NoArgsConstructor class Address { private Street street; public Street getStreet() { return new Street(); } public Optional<Street> getStreetOpt() { return Optional.of(new Street()); } } @Data class Street { private String streetName = "行号代名"; private Integer streetNo = 123; }
输出结果:
Optional : 行号代名
嵌套判断 : 行号代名