「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」
前言
哈喽大家好,我是卡诺,一名致力于成为全栈的全粘工程师!
作为一名javaer,java.lang.NullPointerException
(简称:NPE) 应该是我们最常见的异常之一,为了避免这个问题,通常我们会在可能出现null的地方进行if检测,在某些时候可能造成很深层次的嵌套,导致代码膨胀 (代码数变多) 、可读性变差,维护也变得异常艰难。为了解决这个问题,Java8中引入了Optional类。本章我们将围绕Optional类进行讲解,希望大家通过本章可以对Optional有新的认识!
简介
Optional是一个支持存放泛型类型值的容器对象,其存放的值可以为空,也可以为非空。Optional能通过自身的容器方法判断值是否存在,和获取容器中存放的值。
案例准备
我们准备一个房子(House
),房子有门(Door
),门包含一个类型(Type
),有一个获取类型名称的方法getDoorTypeName
@Data
public class House {
private Door door;
public String getDoorTypeName() {
return door.getType().getName();
}
@Data
static class Door {
private Type type;
}
@Data
static class Type {
private String name;
}
}
NPE演示
获取门的类型名称
public void testNPE(){
System.out.println(new House().getDoorTypeName()); // java.lang.NullPointerException
}
if解决NPE
House中增加getOptimizeDoorTypeName
方法,方法内部进行为空判断
public String getOptimizeDoorTypeName() {
if (door != null) {
Type type = door.getType();
if (type != null) {
return type.getName();
}
}
return "未知类型";
}
上述方法存在一个问题,如果属性嵌套层次比较深,一直用if判断,代码可以说是相当膨胀了,对于这个问题,我们赶快请出本文介绍的主人公Optional
吧!
Optional使用指南
Optional是一个final修饰的类,不能被继承。打开源码查看你会发现它的构造函数也是私有的,说明我们也不能在外部使用关键字new进行创建,当然Java的设计者不会自绝后路,Optional提供了更加方便的of和ofNullable来进行构建Optional对象!
Optional容器创建方法
- 创建方法一览表
方法 | 备注 |
---|---|
public static Optional empty() | 返回一个空容器,内部值为空 |
public static Optional of(T value) | 根据value创建一个容器,value不允许为空,如果为空抛出NPE异常 |
public static Optional ofNullable(T value) | 根据value创建一个容器,value为空等同于empty() 、不为空等同于of() |
- 代码演示
public void testOptional(){
Optional<House> houseEmpty = Optional.empty();
Optional<House> houseOfNullable = Optional.ofNullable(null);
Optional<House> houseOfNotNullable = Optional.ofNullable(new House());
Optional<House> houseOf = Optional.of(new House());
Optional<House> houseOfNull = Optional.of(null); // NPE异常
}
Optional容器对象方法
- 方法一览表
方法 | 备注 |
---|---|
public T get() | 获取Optional的值,值不存在NoSuchElementException异常 |
public boolean isPresent() | 判断Optional值是否存在 |
public void ifPresent(Consumer<? super T> consumer) | 如果Optional值存在,通过传入的consumer对值进行操作,否则不做任何处理 |
public T orElse(T other) | 如果Optional值不为空,则返回该值,否则返回other |
public T orElseGet(Supplier<? extends T> other) | 如果Optional值不为空,则返回该值,否则根据other另外生成一个 |
public T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X | 如果Optional值不为空,则返回该值,否则抛出supplier生成的异常 |
public Optional filter(Predicate<? super T> predicate) | 值存在,且匹配传入的predicate,则返回包含该值的Optional,否则返回一个空Optional |
public Optional map(Function<? super T, ? extends U> mapper) | 如果Optional设置了value,则调用mapper对值进行处理,并返回的Optional包装的U,否则返回空Optional |
public Optional flatMap(Function<? super T, Optional> mapper) | 与map()方法类似,不过它的mapper结果已经是一个Optional,有值的直接返回Optional,否则返回一个空Optional |
- 代码演示
public void testOptionalMethod(){
Optional<House> houseOptional = Optional.of(new House());
House houseHasDoor = new House();
houseHasDoor.setDoor(new House.Door());
Optional<House> houseOptionalHasDoor = Optional.of(houseHasDoor);
Optional<House> houseNullOptional = Optional.empty();
// get
System.out.println("get " + houseOptional.get()); // get House(door=null)
// isPresent
System.out.println("isPresent " + houseOptional.isPresent()); // isPresent true
// ifPresent(Consumer<? super T> consumer)
houseOptional.ifPresent(System.out::println); // House(door=null)
// orElse
House houseElse = houseNullOptional.orElse(new House());
System.out.println("orElse " + houseElse); // orElse House(door=null)
// orElseGet
House houseElseGet = houseNullOptional.orElseGet(House::new);
System.out.println("orElseGet " + houseElseGet); // orElseGet House(door=null)
// filter
Optional<House> house = houseOptional.filter(Objects::nonNull);
Optional<House> houseNull = houseNullOptional.filter(Objects::nonNull);
System.out.println("filter " + house + " " + houseNull); // filter Optional[House(door=null)] Optional.empty
// map, flatMap 返回空
Optional<House.Door> door = houseOptional.map(House::getDoor);
Optional<House.Door> doorFromFlatMap = houseOptional.flatMap(item -> Optional.of(new House.Door()));
System.out.println("empty map " + door + " flatMap " + doorFromFlatMap); // empty map Optional.empty flatMap Optional[House.Door(type=null)]
// map, flatMap 返回有值的容器
Optional<House.Door> doorOptional = houseOptionalHasDoor.map(House::getDoor);
Optional<House.Door> doorFromFlatMapOptional = houseOptionalHasDoor.flatMap(item -> Optional.of(new House.Door()));
System.out.println("has door map " + doorOptional + " flatMap " + doorFromFlatMapOptional); // has door map Optional[House.Door(type=null)] flatMap Optional[House.Door(type=null)]
// orElseThrow
houseOptional.orElseThrow(() -> new RuntimeException()); // House(door=null)
houseNullOptional.orElseThrow(() -> new RuntimeException()); // java.lang.RuntimeException
}
Optional解决NPE
学完Optional,那么回到我们最开始的案例,通过Optional来处理为空问题,House中增加getOptionalDoorTypeName
方法
public String getOptionalDoorTypeName() {
// 将当前door塞入Optional容器
Optional<Door> door = Optional.ofNullable(this.door);
// 将类型塞入Optional容器
Optional<Type> type = Optional.ofNullable(door.orElse(new Door()).getType());
String name = type.orElse(new Type()).getName();
return Objects.isNull(name) ? "未知类型" : name;
}
// 测试
public void testOptionalOptimizeNPE(){
House house = new House();
System.out.println(house.getOptionalDoorTypeName()); // 未知类型
// 设置类型名称
House.Type type = new House.Type();
type.setName("玻璃");
House.Door door = new House.Door();
door.setType(type);
house.setDoor(door);
System.out.println(house.getOptionalDoorTypeName()); // 玻璃
}
更优雅的写法
上述解决方案基于House增加一个getOptionalDoorTypeName
方法,方法内部的实现看起来依然是很臃肿,因此更建议在类的属性本身进行处理,代码如下:
Data
public class OptionalHouse {
private Optional<Door> door = Optional.empty();
public String getDoorTypeName() {
String name = door.orElse(new Door()).getType().orElse(new Type()).getName();
return Objects.isNull(name) ? "未知类型" : name;
}
@Data
public static class Door {
private Optional<Type> type = Optional.empty();
}
@Data
public static class Type {
private String name;
}
}
源码
总结
- 本章主要介绍Optional,Optional是一个支持存放泛型类型值的容器对象,值可以为空,也可以为非空;
- Optional通过静态方法创建Optional对象,部分对象方法如filter、map等用法类似Stream;
- 使用Optional可以有效的减少if的嵌套判断,增加代码的可读性。
关联文章
最后
- 感谢铁子们耐心看到最后,如果大家感觉本文有所帮助,麻烦给个赞👍或关注➕;
- 由于本人技术有限,文章和代码可能存在错误,希望大家评论指出,万分感激🙏;
- 同时也欢迎大家V我一起讨论学习前端、Java知识,一起卷一起进步。