美文网首页
Java8-Optional

Java8-Optional

作者: 一起码 | 来源:发表于2020-04-23 16:54 被阅读0次

    Java8引入了全新的Optional类,主要用来处理空指针异常(NullPointerException)。从本质上说该类属于包含可选值的封装类(wrapper class),因此它既可以包含对象也可以仅仅为空。

    举例说明:

    在 Java 8 之前,凡涉及到访问对象方法或者对象属性的操作,无论数量多寡,都可能导致空指针异常:

    String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
    

    为确保上面实例不出现空指针异常,需对每一个值进行显式的null检查

    if (user != null) {
        Address address = user.getAddress();
        if (address != null) {
            Country country = address.getCountry();
            if (country != null) {
                String isocode = country.getIsocode();
                if (isocode != null) {
                    isocode = isocode.toUpperCase();
                }
            }
        }
    }
    

    但是很多时候会忘记null的检查,即使没有忘记写出上面的代码也将很难维护

    为了解决上述问题,JDK在Java8的时候加入了Optional,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

    JDK提供了三个静态方法来构造一个Optional

    Optional.of(T value)

    该方法通过一个非null的value来构造一个Optional,返回的Optional包含了value这个值,对于该方法,传入的参数一定不能为null,否则会抛出NullPointerException

    Optional.ofNullable(T value)

    该方法和of的区别在于,传入的参数可以为null,与之前Optional javadoc提到的不为null有冲突,原因可以参考ofNullable方法的源码

    public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }
    

    该方法会对传入的参数进行null判断,如果为null,实际上返回的是Optional.empty

    Optional.empty()

    该方法用来构造一个空的Optional,底层代码实现如下

        /**
         * Common instance for {@code empty()}.
         */
        private static final Optional<?> EMPTY = new Optional<>();
    
        /**
         * If non-null, the value; if null, indicates no value is present
         */
        private final T value;
    
        /**
         * Constructs an empty instance.
         *
         * @implNote Generally only one empty instance, {@link Optional#EMPTY},
         * should exist per VM.
         */
        private Optional() {
            this.value = null;
        }
    

    方法使用总结

    前面JavaDoc提到,Optional的isPresent()用来判断是否包含值,get()方法用来获取Optional包含的值,需要注意在Optional.empty上调用get()方法将抛出NoSuchElementException异常

    举例如下,完整代码在文章底部

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

    和之前使用null的判断没啥区别,还需要使用Optional封装value,增加了代码里,实际上这不是使用Optional的正确姿势,下面将详细介绍Optional各个方法的使用

    ifPresent

    底层源码

    public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }
    

    上面获取用户名的例子可以重构成如下代码

    Optional<User> user = Optional.ofNullable(getUserById(userId));
    user.ifPresent(u -> System.out.println(u.getUsername()));
    

    orElse

    底层源码

    public T orElse(T other) {
        return value != null ? value : other;
    }
    

    使用举例

    User user1 = Optional
        .ofNullable(getUserById(nullUserId))
        .orElse(new User("Unknown", "Unknown", 99));
    System.out.println(user1.toString());
    

    orElseGet

    orElseGet与orElse的区别在于,orElseGet方法传入的参数为一个Supplier接口的实现,当Optional有值的时候,返回值,没有值的时候,返回该Supplier的值

    底层源码

    public T orElseGet(Supplier<? extends T> other) {
        return value != null ? value : other.get();
    }
    

    使用举例

    User user2 = Optional
        .ofNullable(getUserById(userId))
        .orElseGet(() -> new User("Unknown", "unknown", 88));
    System.out.println(user2.toString());
    

    orElseThrow

    orElseThrow与orElse方法的区别,orElseThrow方法当Optional中有值的时候返回值,没有值的时候抛出异常,抛出的异常由传入的exceptionSupplier提供

    底层源码

    public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
        if (value != null) {
            return value;
        } else {
            throw exceptionSupplier.get();
        }
    }
    

    使用举例

    try {
        User user3 = Optional
            .ofNullable(getUserById(nullUserId))
            .orElseThrow(() -> new Exception("cannot found user of " + nullUserId));
        } catch (Exception e) {
            e.printStackTrace();
    }
    

    map

    如果当前Optional为Optional.empty,则依旧返回Optional.empty;否则返回一个新的Optional,该Optional包含的是:函数mapper在以value作为输入时的输出值,可以多次使用map操作

    底层源码

    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }
    

    使用举例

    String userName = Optional.ofNullable(getUserById(nullUserId))
        .map(u -> u.getUsername())
        .map(name -> name.toLowerCase())
        .orElse("Unknown");
    System.out.println(userName);
    

    flatMap

    flatMap与map方法的区别在于,map方法参数中函数mapper输出的是值,然后map方法会使用Optional.ofNullable将其包装为Optional,而flatMap要求参数中的函数mapper输出的就是Optional

    底层源码

    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }
    

    使用举例

    Optional<String> userName1 = Optional.ofNullable(getUserById(userId))
        .flatMap(u -> Optional.of(u.getUsername()))
        .flatMap(name -> Optional.of(name.toLowerCase()));
    System.out.println(userName1);
    

    filter

    filter方法接收一个Predicate来对Optional中包含的值进行过滤,如果包含的值满足条件,则还是返回这个Optional,否则返回Optional.empty

    底层源码

    public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent())
            return this;
        else
            return predicate.test(value) ? this : empty();
    }
    

    使用举例

    Optional<String> userName2 = Optional.ofNullable(getUserById(userId))
        .filter(u -> u.getUsername() != "Jack")
        .map(u -> u.getUsername());
    System.out.println(userName2);
    

    完整代码

        public static void main(String[] args) {
    
            int nullUserId = 0;
            int userId = 666;
            Optional<User> user = Optional.ofNullable(getUserById(userId));
            user.ifPresent(u -> System.out.println(u.getUsername()));
    
            User tmpUser = new User();
            user.ifPresent(u -> tmpUser.setUsername(u.getUsername()));
            System.out.println(tmpUser.toString());
    
            user.ifPresent(u -> {int age = u.getAge();
                System.out.println(age);});
    
            User user1 = Optional
                    .ofNullable(getUserById(nullUserId))
                    .orElse(new User("Unknown", "Unknown", 99));
            System.out.println(user1.toString());
    
            User user2 = Optional
                    .ofNullable(getUserById(userId))
                    .orElseGet(() -> new User("Unknown", "unknown", 88));
            System.out.println(user2.toString());
    
            try {
                User user3 = Optional
                        .ofNullable(getUserById(nullUserId))
                        .orElseThrow(() -> new Exception("cannot found user of " + nullUserId));
            } catch (Exception e) {
                e.printStackTrace();
            }
    
            String userName = Optional.ofNullable(getUserById(nullUserId))
                    .map(u -> u.getUsername())
                    .map(name -> name.toLowerCase())
                    .orElse("Unknown");
            System.out.println(userName);
    
            Optional<String> userName1 = Optional.ofNullable(getUserById(userId))
                    .flatMap(u -> Optional.of(u.getUsername()))
                    .flatMap(name -> Optional.of(name.toLowerCase()));
            System.out.println(userName1);
    
            Optional<String> userName2 = Optional.ofNullable(getUserById(userId))
                    .filter(u -> u.getUsername() != "Jack")
                    .map(u -> u.getUsername());
            System.out.println(userName2);
        }
    
        private static User getUserById(int userId) {
            if (666 == userId) {
                return new User("Jack", "Beijing", 18);
            } else {
                return null;
            }
        }
    

    参考

    使用 Optional 处理 null
    了解、接受和利用Java中的Optional

    相关文章

      网友评论

          本文标题:Java8-Optional

          本文链接:https://www.haomeiwen.com/subject/mabaihtx.html