Optional类
引入
NPE
问题就是在开发中经常碰到的NullPointerException
,即空指针问题,Optional
类就是用来优雅解决该问题的方案。
比如大家可能都有这样的经历:调用一个方法得到了返回值却不能直接将返回值作为参数去调用别的方法。我们首先要判断这个返回值是否为null
,只有在非空的前提下才能将其作为其他方法的参数。
以用户类和地址类举例说明其用法:
1public class User {2 private String userName;3 private String phoneNumber;4 private Address address;5 //无参、部分参数、全参数构造器方法...6 //setter、getter方法...7}
1public class Address {2 private String province;3 private String city;4 private String area;5 //无参、部分参数、全参数构造器方法...6 //setter、getter方法...7}
1//这行代码可能会出问题2String province = user.getAddress().getProvince();3System.out.println(province);
这种代码可能会出现空指针问题,在实际开发中,如果不使用Optional
类,极其不优雅的处理方式如下:
1//极其不优雅的处理方式2if (user != null) {//对user对象的null值判断3 Address address = user.getAddress();4 if (address != null) {//对address对象的null值判断5 String province = address.getProvince();6 if (province != null) {//对province对象的null值判断7 System.out.println(province);8 } else {9 System.out.println("province==null");10 }11 }12}
上面的代码保证了代码的第三行、第五行、第七行肯定不会出现空指针,但是这个代码真的是非常的冗长和丑陋。
java.util
包下面的Optional
类提供了一套API来处理一个对象是否为null
值的问题。
源码解读及各API的使用
部分源码:
1public final class Optional<T> {2 //value为null的Optinal对象,类加载的时候就已经初始化完成该Optional对象3 private static final Optional<?> EMPTY = new Optional<>();4 //存储需要判断null的对象5 private final T value;6 //无参构造函数7 private Optional() {8 this.value = null;9 }10 //有参构造函数11 private Optional(T value) {12 this.value = Objects.requireNonNull(value);13 }14}
其本质是内部有一个泛型容器存储外部需要判断null
值的对象,同时提供了两个私有的构造函数,不能被外部所调用,只能由类内部的函数调用
- 无参数的构造函数提供一个
value=null
的Optional
对象 - 有参数的构造函数提供一个
value
一定不能为null
的Optional
对象,因为它调用了Objects
类的requireNonNull
方法。
1//源码2public final class Objects {3 public static <T> T requireNonNull(T obj) {4 if (obj == null)5 throw new NullPointerException();6 return obj;7 }8}
of
1//源码2public static <T> Optional<T> of(T value) {3 return new Optional<>(value);4}
这是一个静态方法,调用有参数的构造函数,返回的是value
值一定不为null
的Optional
对象,因为有参数的构造方法底层调用了Objects
的requireNonNull
方法,如果传入的value
为null
值,那么一定会报空指针异常。不允许value
为null
,实际开发中不常用。
empty
1//源码2public static<T> Optional<T> empty() {3 @SuppressWarnings("unchecked")4 Optional<T> t = (Optional<T>) EMPTY;5 return t;6}
这是一个静态方法,直接将类初始化时加载的value
为null
的Optional
对象给用户。
ofNullable
1//源码2public static <T> Optional<T> ofNullable(T value) {3 return value == null ? empty() : of(value);4}
这是一个静态方法,代表value
值是可为空的。如果为null
值,那么返回一个value
为null
的Optional
对象;如果不为null
值,那么返回一个value
不为null
的Option
对象。允许value
为null
,实际开发中常用。
与of
的区别:当value
值为null
时,of
会报NullPointerException
异常;ofNullable
不会throw Exception
,ofNullable
直接返回一个EMPTY
对象(value
为null
的Optional
对象)。
::: tip 那是不是意味着,我们在项目中只用ofNullable函数而不用of函数呢?
- 不是的,一个东西存在那么自然有存在的价值。
- 当我们在运行过程中,不想隐藏
NullPointerException
,而是要立即报告,这种情况下就用of
函数。 - 但是不得不承认,这样的场景真的很少。
:::
orElse
1//源码2public T orElse(T other) {3 return value != null ? value : other;4}
这是一个实例方法,会首先判断调用它的Optional
对象中的value
值,如果为不为null
,那么就返回该value
值,如果为null
,就返回传入的other
对象。
1@Test2public void orElseTest() {3 //1 user不为null4 User user1 = new User();5 user1.setUserName("1");6 //2 user为null7 User user2 = null;8 //测试第一种9 User user3 = Optional.ofNullable(user1).orElse(new User("2"));10 System.out.println(user3.getUserName());11 //测试第二种12 User user4 = Optional.ofNullable(user2).orElse(new User("3"));13 System.out.println(user4.getUserName());14}15//运行结果2 collapsed lines
161173
orElseGet
1//源码2public T orElseGet(Supplier<? extends T> other) {3 return value != null ? value : other.get();4}
1@Test2public void orElseGetTest() {3 //1 user不为null4 User user1 = new User();5 user1.setUserName("1");6 //2 user为null7 User user2 = null;8 //测试第一种9 User user3 = Optional.ofNullable(user1).orElseGet(new Supplier<User>() {10 @Override11 public User get() {12 return new User("2");13 }14 });15 System.out.println(user3.getUserName());12 collapsed lines
16 //测试第二种17 User user4 = Optional.ofNullable(user2).orElseGet(new Supplier<User>() {18 @Override19 public User get() {20 return new User("3");21 }22 });23 System.out.println(user4.getUserName());24}25//运行结果261273
该方法与orElse
方法类似,只不过传入的other
对象可以通过一个提供者函数式接口提供,这里可以改成lambda
表达式的形式。为了方便对代码的理解,所以上面写的测试代码稍显复杂,实际开发中可以使用lambda
表达式简化。
orElseThrow
1//源码2public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {3 if (value != null) {4 return value;5 } else {6 throw exceptionSupplier.get();7 }8}
如果value
值不为null
,那么直接返回,如果value
值为null
,可以自定义业务逻辑功能说明语句抛出异常,不影响后续代码执行。
1@Test2public void orElseThrowTest() {3 //1 user不为null4 User user1 = new User();5 user1.setUserName("1");6 //2 user为null7 User user2 = null;8 //测试第一种9 User user3 = null;10 try {11 user3 = Optional.ofNullable(user1).orElseThrow(new Supplier<Throwable>() {12 @Override13 public Throwable get() {14 return new Throwable("user1为null的业务逻辑功能说明");15 }26 collapsed lines
16 });17 } catch (Throwable throwable) {18 throwable.printStackTrace();19 }20 System.out.println(user3.getUserName());21 //测试第二种22 User user4 = null;23 try {24 user4 = Optional.ofNullable(user2).orElseThrow(new Supplier<Throwable>() {25 @Override26 public Throwable get() {27 return new Throwable("user2为null的业务逻辑功能说明");28 }29 });30 } catch (Throwable throwable) {31 throwable.printStackTrace();32 }33 System.out.println("不影响后续业务逻辑的执行...");34}35//执行结果36137java.lang.Throwable: user2为null的业务逻辑功能说明38 at com.ouc.ystong.test.TestMain$4.get(TestMain.java:82)39 at com.ouc.ystong.test.TestMain$4.get(TestMain.java:79)40 ...41不影响后续业务逻辑的执行...
map
1//源码2public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {3 Objects.requireNonNull(mapper);4 if (!isPresent())5 return empty();6 else {7 return Optional.ofNullable(mapper.apply(value));8 }9}
如果有值,则对其执行调用mapper
函数得到返回值,将创建包含mapper
返回值的Optional
对象作为map
方法返回值,否则返回空Optional
对象。
1@Test2public void mapTest() {3 //1 user不为null4 User user1 = new User();5 user1.setUserName("1");6 //2 user为null7 User user2 = null;8 //测试第一种9 Optional<String> stringOptional1 = Optional.ofNullable(user1).map(new Function<User, String>() {10 @Override11 public String apply(User user) {12 return user.getUserName() + "xiaotongtong";13 }14 });15 System.out.println(stringOptional1);12 collapsed lines
16 //测试第二种17 Optional<String> stringOptional2 = Optional.ofNullable(user2).map(new Function<User, String>() {18 @Override19 public String apply(User user) {20 return user.getUserName() + "xiaotongtong";21 }22 });23 System.out.println(stringOptional2);24}25//执行结果26Optional[1xiaotongtong]27Optional.empty
map
方法用来对Optional
实例的值执行一系列操作。通过一组实现了Function
接口的lambda
表达式传入操作。
flatMap
1//源码2public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {3 Objects.requireNonNull(mapper);4 if (!isPresent())5 return empty();6 else {7 return Objects.requireNonNull(mapper.apply(value));8 }9}
如果有值,为其执行mapper
函数返回Optional
对象类型返回值,否则返回空Optional
对象。flatMap
与map
方法类似,区别在于flatMap
中的mapper
返回值必须是Optional
对象。调用结束时,flatMap
不会对结果用Optional
封装。
1@Test2public void flatMapTest() {3 //1 user不为null4 User user1 = new User();5 user1.setUserName("1");6 //2 user为null7 User user2 = null;8 //测试第一种9 Optional<String> stringOptional1 = Optional.ofNullable(user1).flatMap(new Function<User, Optional<String>>() {10 @Override11 public Optional<String> apply(User user) {12 return Optional.ofNullable(user.getUserName() + "xiaotongtong");13 }14 });15 System.out.println(stringOptional1);12 collapsed lines
16 //测试第二种17 Optional<String> stringOptional2 = Optional.ofNullable(user2).flatMap(new Function<User, Optional<String>>() {18 @Override19 public Optional<String> apply(User user) {20 return Optional.ofNullable(user.getUserName() + "xiaotongtong");21 }22 });23 System.out.println(stringOptional2);24}25//执行结果26Optional[1xiaotongtong]27Optional.empty
flatMap
方法与map
方法类似,区别在于mapper
函数的返回值不同。map
方法的mapper
函数返回值可以是任何类型T
,而flatMap
方法的mapper
函数必须是Optional
对象。
filter
1//源码2public Optional<T> filter(Predicate<? super T> predicate) {3 Objects.requireNonNull(predicate);4 if (!isPresent())5 return this;6 else7 return predicate.test(value) ? this : empty();8}
如果有值并且满足断言条件返回包含该值的Optional
对象,否则返回空Optional
对象。
1@Test2public void filterTest() {3
4 Optional<String> stringOptional1 = Optional.of("xiaotongtong").filter(new Predicate<String>() {5 @Override6 public boolean test(String s) {7 return s.toCharArray().length > 8;8 }9 });10 //满足条件,返回包含该值的Option对象11 System.out.println(stringOptional1);12 Optional<String> stringOptional2 = Optional.of("xiaotongtong").filter(new Predicate<String>() {13 @Override14 public boolean test(String s) {15 return s.toCharArray().length > 15;8 collapsed lines
16 }17 });18 //不满足条件,返回值为空的Optional对象19 System.out.println(stringOptional2);20}21//执行结果22Optional[xiaotongtong]23Optional.empty
isPresent
1//源码2public boolean isPresent() {3 return value != null;4}
ifPresent
1//源码2public void ifPresent(Consumer<? super T> consumer) {3 if (value != null)4 consumer.accept(value);5}
get
1//源码2public T get() {3 if (value == null) {4 throw new NoSuchElementException("No value present");5 }6 return value;7}
equals
1//源码2@Override3public boolean equals(Object obj) {4 //两者指向的内存地址相同,那么Optional对象肯定相同5 if (this == obj) {6 return true;7 }8 //如果obj不是Optional类型的,那肯定是不相同的9 if (!(obj instanceof Optional)) {10 return false;11 }12 //已经确定是Optional类型的,所以可以强转13 Optional<?> other = (Optional<?>) obj;14 //比较两个Optional的value值是不是相同15 return Objects.equals(value, other.value);1 collapsed line
16}
hashCode
1//源码2@Override3public int hashCode() {4 return Objects.hashCode(value);5}
toString
1//源码2@Override3public String toString() {4 return value != null5 ? String.format("Optional[%s]", value)6 : "Optional.empty";7}
使用例子
- 基本使用
1public class OptionalDemo {2
3 public static void main(String[] args) {4 //创建Optional实例,也可以通过方法返回值得到。5 Optional<String> name = Optional.of("Sanaulla");6
7 //创建没有值的Optional实例,例如值为'null'8 Optional empty = Optional.ofNullable(null);9
10 //isPresent方法用来检查Optional实例是否有值。11 if (name.isPresent()) {12 //调用get()返回Optional值。13 System.out.println(name.get());14 }15
53 collapsed lines
16 try {17 //在Optional实例上调用get()抛出NoSuchElementException。18 System.out.println(empty.get());19 } catch (NoSuchElementException ex) {20 System.out.println(ex.getMessage());21 }22
23 //ifPresent方法接受lambda表达式参数。24 //如果Optional值不为空,lambda表达式会处理并在其上执行操作。25 name.ifPresent((value) -> {26 System.out.println("The length of the value is: " + value.length());27 });28
29 //如果有值orElse方法会返回Optional实例,否则返回传入的错误信息。30 System.out.println(empty.orElse("There is no value present!"));31 System.out.println(name.orElse("There is some value!"));32
33 //orElseGet与orElse类似,区别在于传入的默认值。34 //orElseGet接受lambda表达式生成默认值。35 System.out.println(empty.orElseGet(() -> "Default Value"));36 System.out.println(name.orElseGet(() -> "Default Value"));37
38 try {39 //orElseThrow与orElse方法类似,区别在于返回值。40 //orElseThrow抛出由传入的lambda表达式/方法生成异常。41 empty.orElseThrow(ValueAbsentException::new);42 } catch (Throwable ex) {43 System.out.println(ex.getMessage());44 }45
46 //map方法通过传入的lambda表达式修改Optonal实例默认值。47 //lambda表达式返回值会包装为Optional实例。48 Optional<String> upperName = name.map((value) -> value.toUpperCase());49 System.out.println(upperName.orElse("No value found"));50
51 //flatMap与map(Funtion)非常相似,区别在于lambda表达式的返回值。52 //map方法的lambda表达式返回值可以是任何类型,但是返回值会包装成Optional实例。53 //但是flatMap方法的lambda返回值总是Optional类型。54 upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));55 System.out.println(upperName.orElse("No value found"));56
57 //filter方法检查Optiona值是否满足给定条件。58 //如果满足返回Optional实例值,否则返回空Optional。59 Optional<String> longName = name.filter((value) -> value.length() > 6);60 System.out.println(longName.orElse("The name is less than 6 characters"));61
62 //另一个示例,Optional值不满足给定条件。63 Optional<String> anotherName = Optional.of("Sana");64 Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);65 System.out.println(shortName.orElse("The name is less than 6 characters"));66
67 }68}
运行结果:
1Sanaulla2No value present3The length of the value is: 84There is no value present!5Sanaulla6Default Value7Sanaulla8No value present in the Optional instance9SANAULLA10SANAULLA11Sanaulla12The name is less than 6 characters
- 在
Java8
中提高对象的null
值安全性
假设有如下的类层次结构:
1class Outer {2 Nested nested;3 Nested getNested() {4 return nested;5 }6}7class Nested {8 Inner inner;9 Inner getInner() {10 return inner;11 }12}13class Inner {14 String foo;15 String getFoo() {3 collapsed lines
16 return foo;17 }18}
解决这种结构的深层嵌套路径是有点麻烦的,我们必须编写一堆null
检查来确保不会导致一个 NullPointerException
。
我们可以通过利用Optional
类型来摆脱所有这些null
检查。map
方法接收一个Function
类型的lambda
表达式,并自动将每个function
的结果包装成一个Optional
对象,这使我们能够在一行中进行多个 map
操作。
1Optional.of(new Outer())2 .map(Outer::getNested)3 .map(Nested::getInner)4 .map(Inner::getFoo)5 .ifPresent(System.out::println);
使用总结
使用Optional
工具类判断一个对象的NPE
问题,一定要先通过其静态方法(of、empty、ofNullable)
获得Optional
对象,进而通过一些实例方法进行一系列的操作获得最后的对象。
API方法名称 | 用处 |
---|---|
of | 为非null 的值创建一个Optional 对象。 |
empty | 为null 的值创建一个Optional 对象。 |
ofNullable | 为指定的值创建一个Optional 对象,如果指定的值为null ,则返回一个空的Optional 对象。 |
isPresent | 如果值不为null ,则返回true ,否则返回false 。 |
get | 如果Optional 对象的值并不为空则将其返回,否则抛出NoSuchElementException 。 |
ifPresent | 如果Optional 对象有值(不为空)则为其调用Consumer ,否则不做处理 |
orElse | 如果有值则将其返回,否则返回指定的其它值。 |
orElseGet | orElseGet 方法可以接受Supplier 接口的实现用来生成默认值。 |
orElseThrow | 如果有值则将其返回,否则抛出Supplier 接口创建的异常。 |
map | 如果有值,则对其执行调用mapper 函数得到返回值,并且将创建包含mapper 返回值的Optional 对象作为map 方法返回值,否则返回空Optional 对象。 |
flatMap | 如果有值,为其执行mapper 函数返回Optional 类型返回值,否则返回空Optional 对象。flatMap 与map 方法类似,区别在于flatMap 中的mapper 返回值必须是Optional 对象。调用结束时,flatMap 不会对结果用Optional 封装。 |
filter | 如果有值并且满足断言条件返回包含该值的Optional 对象,否则返回空Optional 对象。 |