JDK8新特性之:Optional

痛点

在java编码过程中,大家碰到的最多的异常是什么,我相信必然这货NullPointerException必然是排行第一的。那我们在平时编码中,有各种编码规范与其相关,比如时时的判断null,方法禁止返回null等,例如

public void bindUserToRole(User user) {
    if (user != null) {
        String roleId = user.getRoleId();
        if (roleId != null) {
            Role role = roleDao.findOne(roleId);
            if (role != null) {
                role.setUserId(user.getUserId());
                roleDao.save(role);
            }
        }
    }
}

或者

public String bindUserToRole(User user) {
    if (user == null) {
        return;
    }

    String roleId = user.getRoleId();
    if (roleId == null) {
        return;
    }

    Role = roleDao.findOne(roleId);
    if (role != null) {
        role.setUserId(user.getUserId());
        roleDao.save(role);
    }
}

为了防止NullPointerException,好好的代码写成这个鸟样,或许下面的会看上去比较顺眼一点,但是大体还是一样的。
其实我们有一种更为优雅的方式来完成上面的功能,如下

Optional roleOpt = Optional.ofNullable(user).map(User::getRoleId);
if(roleOpt.isPresent()){
    ....
}

这样,我们仅需要对我们关心的做一次校验,省却了前面的一系列的检验操作。

Optional的引入

基于上述的一些原因,在JDK8中,引入了一个新的类java.util.Optional,来避免这类问题的处理。
先看下类的说明

A container object which may or may not contain a non-null value.If a value is present, isPresent()will return true andget() will return the value.

这里面说明这是一个可以包含null或者非null的容器,其最基本的两个操作就是isPresent()get(),基本用起来就是这样

if( oneOptional.isPresent() ){
     String s = oneOptional.get();
    ....
}

首先我们看下这个类中包含哪些方法,如下

JDK8新特性之:Optional_第1张图片
image.png

首先,其有一个成员变量和一个定义的常量如下

   private static final Optional EMPTY = new Optional<>();
   private final T value;

其中value表示其封装的真实的对象,EMPTY是定义的一个表示空的常量。构造方法很很简单,两个常见的私有构造方法Optional()Optional(T),其中不带参的默认设置valuenull,带参的构造函数,不允许传null

Optional类包含3个静态方法生成Optional对象,分别为

  • Optional empty()
    生成一个空Optional对象,其valuenull
  • Optional of(T value)
    调用Optional(T)构造方法,其value不允许为null
  • Optional ofNullable(T value)
    Optional of(T value)的差别是其传入的value允许为null

下面简单介绍一下里面的各个方法和一些简单的使用示例

  • get()
    返回value,若为null则抛出异常NoSuchElementException
  • isPresent()
    判断当前的value是否为null
  • ifPresent(Consumer consumer)
    该方法支持传入一个Consumer对象,当value不为null的时候调用,为null则不做任何操作,示例如下
    public void testIfPresent(){
        Optional optional=Optional.of("zh");
        optional.ifPresent(new Consumer() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
    }

配合lambda使用,代码更为简洁

    public void testIfPresent(){
        Optional optional=Optional.of("zh");
        optional.ifPresent(s -> System.out.println(s));
    }
  • filter(Predicate predicate)
    顾名思义,这个方法的作用就是filter(过滤),该方法用于过滤一个Optional对象,通过传入的Predicate对象中的一个test方法,例如
        Optional optional=Optional.of("aa");
        Optional optionalResult=optional.filter(s -> s.startsWith("a"));
        System.out.println(optionalResult);

输出结果是

Optional[aa]

由于其返回仍是一个Optional对象,我们可以有如下较为优雅的写法

Optional optionalResult=optional.filter(s -> s.startsWith("a"))
                .filter(s -> s.length()==2)
                .filter(s -> s.endsWith("a"));
  • map(Function mapper)
    此方法支持一个Function参数,在这个Function里面可以对这个Optional对象做一些操作或者改变,其返回值仍为一个Optional类型,例如
Optional optional=Optional.of("aa");
        Optional result= optional.map(s -> s.toUpperCase())
                .map(s->s.length());

输出结果为

Optional[2]

map提供一种优雅的流式的方式来替代先前繁杂的if判断和数据处理

  • flatMap(Function> mapper)
    map类似,只不过需要我们手动将方法的返回,封装成Optional对象,如
Optional result= optional.flatMap(s -> Optional.of(s.toUpperCase()))
                .flatMap(s -> Optional.of(s.length()));

其余功能与map一致 。

  • orElse(T other)
    这个方法较为简单,参数为一个默认值,即若valuenull,则返回默认值
        Optional optional=Optional.of("aa");
        System.out.println(optional.orElse("bb"));
        Optional optional1=Optional.ofNullable(null);
        System.out.println(optional1.orElse("bb"));

输出为

aa
bb
  • orElseGet(Supplier other)
    提供一个Supplier入参,改Supplier提供一个默认值,例如
    Optional optional=Optional.ofNullable(null);
    System.out.println(optional.orElseGet(() -> "aaa"));
  • orElseThrow(Supplier exceptionSupplier)
    顾名思义,若为null则抛出异常
        Optional optional=Optional.ofNullable(null);
        System.out.println(optional.orElseThrow(() -> new RuntimeException()));

使用场合注意

Reports calls to java.util.Optional.get() without first checking with a isPresent() call if a value is available. If the Optional does not contain a value, get() will throw an exception.

Optional.get() 前不事先用 isPresent() 检查值是否可用. 假如 Optional 不包含一个值, get() 将会抛出一个异常

Reports any uses of java.util.Optional, java.util.OptionalDouble, java.util.OptionalInt, java.util.OptionalLong or com.google.common.base.Optional as the type for a field or a parameter. Optional was designed to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result”. Using a field with type java.util.Optional is also problematic if the class needs to be Serializable, which java.util.Optional is not

使用任何像 Optional 的类型作为字段或方法参数都是不可取的. Optional 只设计为类库方法的, 可明确表示可能无值情况下的返回类型. Optional 类型不可被序列化, 用作字段类型会出问题的

借鉴

  • https://lw900925.github.io/java/java8-optional.html
  • http://www.importnew.com/22060.html

你可能感兴趣的:(JDK8新特性之:Optional)