Java8新特性(二):Optional

307 阅读4分钟

Optional

引言

代码问题

在写程序的时候一般都遇到过 NullPointerException,所以经常会对程序进行非空的判断:

User user = getUserById(id);
if (user != null) {
    String username = user.getUsername();
    System.out.println("Username is: " + username); 
}

代码中出现大量判断是否null逻辑,代码繁琐,阅读性差,为了解决这种尴尬的处境,JDK 终于在 Java8 的时候加入了 Optional 类

优化后

Optional<User> user = Optional.ofNullable(getUserById(id));
if (user.isPresent()) {
    String username = user.get().getUsername();
    System.out.println("Username is: " + username); 
}

什么是 Optional

  • javadoc 介绍:
    • A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.
  • 这是一个可以包含或者不包含非 null 值的容器。如果值存在则 isPresent()方法会返回 true,调用 get() 方法会返回该对象。

Optional构造方法

Optional.of(T value)

将指定值用 Optional 封装之后返回,如果该值为 null,则抛出一个 NullPointerException 异常。

Optional.ofNullable(T value)

将指定值用 Optional 封装之后返回,如果该值为 null,则返回一个空的 Optional 对象。

Optional.empty()

创建一个空的 Optional 实例。

Optional常用API

isPresent

如果值存在返回true,否则返回false

ifPresent

如果Optional实例有值则为其调用consumer ,否则不做处理。 要理解ifPresent方法,首先需要了解Consumer类。简答地说,Consumer类包含一个抽象方法。该抽象方法对传入的值进行处理,但没有返回值。 Java8支持不用接口直接通过lambda表达式传入参数。 如果Optional实例有值,调用ifPresent()可以接受接口段或lambda表达式。

Optional.get()

如果该值存在,将该值用 Optional 封装返回,否则抛出一个 NoSuchElementException 异常。

orElse(T t)

如果调用对象包含值,返回该值,否则返回t。

orElseGet(Supplier s)

如果调用对象包含值,返回该值,否则返回 s 获取的值。

orElseThrow()

它会在对象为空的时候抛出异常。

map(Function f)

如果值存在,就对该值执行提供的 mapping 函数调用。

flatMap(Function mapper)

如果值存在,就对该值执行提供的mapping 函数调用,返回一个 Optional 类型的值,否则就返回一个空的 Optional 对象。

代码

public class TestOptional {
    public static void main(String[] args) {
        /* of of方法通过工厂方法创建Optional类。需要注意的是,创建对象时传入的参数不能为null。如果传入参数为null,则抛出NullPointerException 。*/
        //调用工厂方法创建Optional实例
        Optional<String> name = Optional.of("YanWei");
        //传入参数为null,抛出NullPointerException.
        //Optional<String> someNull = Optional.of(null);

        /*opNullable 为指定的值创建一个Optional,如果指定的值为null,则返回一个空的Optional*/
        Optional empty = Optional.ofNullable(null);


        Optional<String> empty2 = Optional.empty();
        System.out.println(empty2.isPresent());

        /*isPresent如果值存在返回true,否则返回false。*/
        if (name.isPresent()) {
            System.out.println(name.get());//输出YanWei
        }
        if (empty.isPresent()) {
            System.out.println(empty.get());//输出YanWei
        }

        /*get如果Optional有值则将其返回,否则抛出NoSuchElementException。*/
        try {
            System.out.println(empty.get());
        } catch (NoSuchElementException ex) {
            System.err.println(ex.getMessage());
        }

        /*ifPresent如果Optional实例有值则为其调用consumer,否则不做处理*/
        name.ifPresent((value) -> {
            System.out.println("The length of the value is: " + value.length());
        });

        /*orElse如果有值则将其返回,否则返回指定的其它值。*/
        System.out.println(empty.orElse("There is no value present!"));
        System.out.println(name.orElse("There is some value!"));

        /**
        *orElseGet
         * orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,
         * orElseGet方法可以接受Supplier接口的实现用来生成默认值
        * */
        System.out.println(empty.orElseGet(() -> "Default Value"));
        System.out.println(name.orElseGet(String::new));

        /*orElseThrow*/
        //如果有值则将其返回,否则抛出supplier接口创建的异常。
        //在orElseGet方法中,我们传入一个Supplier接口。然而,在orElseThrow中我们可以传入一个lambda表达式或方法,如果值不存在来抛出异常。
        try {
            empty.orElseThrow(IllegalArgumentException::new);
        } catch (Throwable ex) {
            System.out.println("error:" + ex.getMessage());
        }

        /*map*/
       /* 如果有值,则对其执行调用mapping函数得到返回值。如果返回值不为null,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional。
        map方法用来对Optional实例的值执行一系列操作。通过一组实现了Function接口的lambda表达式传入操作。*/
        Optional<String> upperName = name.map((value) -> value.toUpperCase());
        System.out.println(upperName.orElse("No value found"));

        /*flatMap*/
        /*如果有值,为其执行mapping函数返回Optional类型返回值,否则返回空Optional。
        flatMap方法与map方法类似,区别在于mapping函数的返回值不同。map方法的mapping函数返回值可以是任何类型T,
        而flatMap方法的mapping函数必须是Optional。*/
        upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
        System.out.println(upperName.get());

        /*filter*/
       /* 如果有值并且满足断言条件返回包含该值的Optional,否则返回空Optional。
        这里可以传入一个lambda表达式。对于filter函数我们应该传入实现了Predicate接口的lambda表达式。*/
        List<String> names = Arrays.asList("YanWei","YanTian");
        for(String s:names)
        {
            Optional<String> nameLenLessThan7 = Optional.of(s).filter((value) -> value.length() < 7);
            System.out.println(nameLenLessThan7.orElse("The name is more than 6 characters"));
        }
    }
}

结果

false
YanWei
No value present
The length of the value is: 6
There is no value present!
YanWei
Default Value
YanWei
error:null
YANWEI
YANWEI
YanWei

源码下载

本文章例子源码

Java8新特性系列