JDK8系列之Optional API应该怎样用?
Posted smileNicky
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK8系列之Optional API应该怎样用?相关的知识,希望对你有一定的参考价值。
JDK8系列之Optional API应该怎样用?
1、Optional的简单介绍
在前面的章节的学习中,我们学习了jdk8的新特性,lambada表达式、方法引用、函数式接口等等,接着本博客继续JDK8的一个比较重要的特性,JDK8中Optional,jdk8设计这个Optional的目的就是为了避免开发中很常见的NullPointerException
Optional 是 Java 实现函数式编程的保障一步,并且帮助在范式中实现
2、为什么Optional可以避免空指针?
看下Optional源码,为什么Optional可以避免NullPointerException?里面可以找到如下代码:
/**
* Returns an {@code Optional} describing the specified value, if non-null,
* otherwise returns an empty {@code Optional}.
*
* @param <T> the class of the value
* @param value the possibly-null value to describe
* @return an {@code Optional} with a present value if the specified value
* is non-null, otherwise an empty {@code Optional}
*/
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
//...
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
value
值为null的情况,返回empty()
,其实也就是返回一个Optional对象,如下代码,直接new Optional<>()
/**
* Common instance for {@code empty()}.
*/
private static final Optional<?> EMPTY = new Optional<>();
而另外一个方法Optional.of()
,
/**
* Returns an {@code Optional} with the specified present non-null value.
*
* @param <T> the class of the value
* @param value the value to be present, which must be non-null
* @return an {@code Optional} with the value present
* @throws NullPointerException if value is null
*/
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
next,可以看出如果传入T value的值为null,还是有空指针异常的
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
3、创建Optional实例
- 使用 empty() 方法创建一个空的 Optional。
// 使用 empty() 方法创建一个空的 Optional
Optional<User> empOpt = Optional.empty();
empOpt.get();
- 使用 of() 方法创建包含值的 Optional,不可以传入一个null值
User user = new User();
Optional<User> opt = Optional.of(user);
- 使用 ofNullable() 方法创建包含值的 Optional,可以传入一个null值
User userObj = null;
Optional<User> userOptional = Optional.ofNullable(userObj);
4、访问 Optional 对象的值
获取Optional对象的值,使用 get() 方法获取值
String name = "jack";
Optional<String> strOpt = Optional.ofNullable(name);
String getStr = strOpt.get();
System.out.println(getStr);
看起来是很正常的,如果传入一个null值,会抛异常java.util.NoSuchElementException: No value present
,所以需要使用ifPresent,避免userInfo值为null的情况
City city = new City("上海");
Address address = new Address("200000",city);
User userInfo = new User("jack", "15588899988" , "123@foxmail.com", address);
Optional.ofNullable(userInfo).ifPresent(us -> System.out.println(us.toString()));
- isPresent不建议这样写,语法上来说,也是可以的,不过设计者的目的是使用表达式语言简洁java语法
Optional<User> userOptiion = Optional.ofNullable(userInfo);
if (userOptiion.isPresent()) {
User userInfomation = userOptiion.get();
}
5、Optional返回默认值
- 使用 orElse() 返回默认值,如果有值则返回该值,没数据返回默认值
//使用 orElse() 返回默认值,如果有值则返回该值,没数据返回默认值
User tUser = null;
System.out.println("using orElse");
tUser = Optional.ofNullable(tUser).orElse(defaultUserInfo());
System.out.println("default user information:"+tUser.toString());
- 使用 orElseGet() 返回默认值 ,这个方法会在有值的时候返回值,如果没有值,它会执行作为参数传入的 Supplier(供应者) 函数式接口
User teUser = null;
System.out.println("using orElseGet");
teUser = Optional.ofNullable(teUser).orElseGet(() -> defaultUserInfo());
System.out.println("default user information:"+teUser.toString());
defaultUserInfo,返回默认用户数据
protected static User defaultUserInfo(){
System.out.println("create default user information!");
City city = new City("上海");
Address address = new Address("200000",city);
User userInfo = new User("jack", "15588899988" , "123@foxmail.com", address);
return userInfo;
}
乍一看,这两个方法的作用好像都一样?有什么区别?还是通过例子进行验证:
①、传入的value值为null的情况,对比orElse、orElseGet
User tUser = null;
System.out.println("using orElse");
tUser = Optional.ofNullable(tUser).orElse(defaultUserInfo());
System.out.println("default user information:"+tUser.toString());
User teUser = null;
System.out.println("using orElseGet");
teUser = Optional.ofNullable(teUser).orElseGet(() -> defaultUserInfo());
System.out.println("default user information:"+teUser.toString());
可以看出,orElse、orElseGet都调用了默认方法
using orElse
create default user information!
using orElseGet
create default user information!
②、传入的value不为null的情况,可以看出orElse还是调用了默认方法,而orElseGet没有调用默认方法
User tUser = new User("jack", "15588899988" , "123@foxmail.com", address);
System.out.println("using orElse");
tUser = Optional.ofNullable(tUser).orElse(defaultUserInfo());
System.out.println("default user information:"+tUser.toString());
User teUser = new User("jack", "15588899988" , "123@foxmail.com", address);
System.out.println("using orElseGet");
teUser = Optional.ofNullable(teUser).orElseGet(() -> defaultUserInfo());
System.out.println("default user information:"+teUser.toString());
using orElse
create default user information!
using orElseGet
ok,从例子就可以很明显得看出区别,orElse、orElseGet在传入的值为null的情况,都调用了默认方法。在传入的值不为null的情况,orElse调用了默认方法,而orElseGet没有调用默认方法
6、Optional返回异常
Optional 还定义了 orElseThrow() API,它会在对象为空的时候抛出异常,而不是返回备选的值
User u = null;
User userData = Optional.ofNullable(u).orElseThrow(() -> new IllegalArgumentException());
7、Optional转换值
用于转换数据的,可以使用 map() 方法转换 Optional 的值
map例子:
User us = new User("tom", "15588899988" , "123@foxmail.com", null,"");
String email = Optional.ofNullable(us).map(userInfor -> userInfor.getEmail()).orElse("defaulEmail@foxmail.com");
如果类中的数据是用Optional进行封装的,如下:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.experimental.Accessors;
import java.util.Optional;
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
// ...
private String position;
public Optional<String> getPosition() {
return Optional.ofNullable(position);
}
}
用 flatMap() 时,用它作为参数,返回的值是解除包装的 String 值
String position = Optional.ofNullable(us).flatMap(use -> use.getPosition()).orElse("default");
8、Optional过滤值
除了转换值之外,Optional 类也提供了按条件“过滤”值的方法filter,filter() 接受一个 Predicate 参数
Optional<User> uoptional = Optional.ofNullable(us).filter(euser -> euser.getEmail()!=null && euser.getEmail().contains("@"));
System.out.println(uoptional.get().toString());
9、典型的Optional例子
用一个嵌套调用的典型例子:
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User {
private String name;
private String mobiTel;
private String email;
private Address address;
private String position;
public Optional<String> getPosition() {
return Optional.ofNullable(position);
}
}
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Address {
private String emailCode;
private City city;
}
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class City {
private String cityName;
}
画图表示类关系:
uml类图:
jdk7写法:
/**
* the example of jdk7 getCityName .<br>
* @Author nicky.ma
* @Date 2021/07/20 14:39
* @Param [user]
* @return java.lang.String
*/
protected static String getCityName(User user) {
if (user != null) {
Address address = user.getAddress();
if (address != null) {
City city = address.getCity();
if (city != null) {
return city.getCityName();
}
}
}
throw new IllegalArgumentException("取值错误");
}
jdk8写法:
/**
* the example of jdk8 getCityName .<br>
* @Author nicky.ma
* @Date 2021/07/20 14:38
* @Param [user]
* @return java.lang.String
*/
protected static String obtainCityName(User user) {
return Optional.ofNullable(user)
.map(u -> u.getAddress())
.map(a -> a.getCity())
.map(c -> c.getCityName())
.orElseThrow(() -> new IllegalArgumentException("取值错误"));
}
10、附录参考资料
以上是关于JDK8系列之Optional API应该怎样用?的主要内容,如果未能解决你的问题,请参考以下文章