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应该怎样用?的主要内容,如果未能解决你的问题,请参考以下文章

JDK8系列之Stream API入门教程和示例

JDK8的Optional类

JDK8版本之日期和时间API详解

JDK8至JDK 11 常用API更新

JDK8至JDK 11 常用API更新

JDK8至JDK 11 常用API更新