zoukankan      html  css  js  c++  java
  • 夯实Java基础(二十四)——Java8新特征之Optional类

    1、概述

    对于Java程序员来说,到目前为止出现次数最多的应该是NullpointException,它是导致Java应用程序失败的最常见原因。之前处理空指针我们必须先通过条件先去判断,然后再确认是否有null值。但是在Java8中,我们可以使用Optional类来解决null值判断问题,其借鉴Google Guava项目的Optional类而引入的一个同名Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。使用Optional类可以避免显式的null值判断(null的防御性检查),避免null导致的NPE(NullPointerException)。

    首先我们来看一段代码示例:

        public static String getName(Student student){
            if (student==null){
                return "null";
            }
            return student.getName();
        }

    这是一个获取学生姓名的方法,方法入参为一个Student对象,为了防止student对象为null, 做了防御性检查:如果值为null,返回"null"。

    再看使用Optional对象的方法:

        public static String getName(Student student){
            Optional<Student> student1 = Optional.ofNullable(student);
            if(student1.isPresent()){
                return "null";
            }
            return student.getName();
        }

    改写成上面这种形式其实和上面的没有什么太大区别,他不是Optional最好的使用方式。

    下面是Optional最优的方式:

        public static String getName(Student student){
            return Optional.ofNullable(student).map(s->s.getName()).orElse("null");
        }

    所以可以看到,Optional类在结合Lambda表达式能够让我们开发出的代码更简洁和优雅。

    2、Optional对象的创建

    创建Optional对象有三种方法,分别是:

    ①、empty():创建一个包装对象值为空的Optional对象

    Optional<Student> optional1= Optional.empty();

    ②、of():创建包装对象值非空的Optional对象

    Optional<String> optional2 = Optional.of("optional2");

    ③、ofNullable():创建包装对象值允许为空的Optional对象

    Optional<Student> optional3 = Optional.ofNullable(null);

    然后我们看下这三个方法在Optional类的部分源码,如下所示:

    public final class Optional<T> {
    
        private static final Optional<?> EMPTY = new Optional<>();
    
        private final T value;
    
        private Optional() {
            this.value = null;
        }
    
        public static<T> Optional<T> empty() {
            @SuppressWarnings("unchecked")
            Optional<T> t = (Optional<T>) EMPTY;
            return t;
        }
    
        private Optional(T value) {
            this.value = Objects.requireNonNull(value);
        }
    
        public static <T> Optional<T> of(T value) {
            return new Optional<>(value);
        }
    
        public static <T> Optional<T> ofNullable(T value) {
            return value == null ? empty() : of(value);
        }
    }

    从上面可以看出,Optional类的两个构造方法都是private型的,因此类外部不能显示的使用new Optional()的方式来创建Optional对象,但是Optional类提供了三个静态方法empty()、of(T value)、ofNullable(T value)来创建Optinal对象。

    3、Optional类常用方法

    1、 get()方法

    简单看下get()方法的源码:

        public T get() {
            if (value == null) {
                throw new NoSuchElementException("No value present");
            }
            return value;
        }
    

    可以看到,get()方法主要用于返回包装对象的实际值,但是如果包装对象值为null,会抛出NoSuchElementException异常。

    2、isPresent()方法

    isPresent()方法的源码:

        public boolean isPresent() {
            return value != null;
        }
    

    可以看到,isPresent()方法用于判断包装对象的值是否非空。

    3、 ifPresent(Consumer<? super T> consumer)方法

    ifPresent()方法的源码:

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

    ifPresent()方法接受一个Consumer对象(消费函数),如果包装对象的值非空,运行Consumer对象的accept()方法,否则不做处理。示例如下:

        public static void printName(Student student)
        {
            Optional.ofNullable(student).ifPresent(s ->  System.out.println(s.getName()));
        }
    

    上述示例用于打印学生姓名,由于ifPresent()方法内部做了null值检查,调用前无需担心NPE问题。

    特别注意:谨慎使用isPresent()和get()方法,尽量多使用下面的filter()、map()、orElse()等方法来发挥Optional的作用。因为使用isPresent()方法会像第一节实现的代码一样:

        public static String getName(Student student){
            Optional<Student> student1 = Optional.ofNullable(student);
            if(student1.isPresent()){
                return "null";
            }
            return student.getName();
        }

    这种用法不但没有减少null的防御性检查,而且增加了Optional包装的过程,违背了Optional设计的初衷,因此开发中要避免这种糟糕的使用。

    4、filter()方法

    filter()方法的源码:

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

    filter()方法接受参数为Predicate对象,用于对Optional对象进行过滤,如果符合Predicate的条件,返回Optional对象本身,否则返回一个空的Optional对象。举例如下:

        public static void filterAge(Student student)
        {
            Optional.ofNullable(student).filter(s ->s.getAge() > 18).ifPresent(s ->  System.out.println("The student age is more than 18."));
        }
    

    上述示例中,实现了年龄大于18的学生的筛选。

    5、map()方法

    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));
            }
        }
    

    map()方法的参数为Function(函数式接口)对象,map()方法将Optional中的包装对象用Function函数进行运算,并包装成新的Optional对象(包装对象的类型可能改变)。举例如下:

        public static Optional<Integer> getAge(Student student)
        {
            return Optional.ofNullable(student).map(s –> s.getAge()); 
        }
    

    上述代码中,先用ofNullable()方法构造一个Optional<Student>对象,然后用map()计算学生的年龄,返回Optional<Integer>对象(如果student为null, 返回map()方法返回一个空的Optinal对象)。

    6、flatMap()方法

    flatMap()方法的源码:

        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));
            }
        }
    

    跟map()方法不同的是,入参Function函数的返回值类型为Optional<U>类型,而不是U类型,这样flatMap()能将一个二维的Optional对象映射成一个一维的对象。

        public static Optional<Integer> getAge(Student student)
        {
            return Optional.ofNullable(student).flatMap(s -> Optional.ofNullable(s.getAge())); 
        }
    

    7、orElse()方法

    orElse()方法的源码:

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

    orElse()方法功能比较简单,即如果包装对象值非空,返回包装对象值,否则返回入参other的值(默认值)。

        public static String getName(Student student)
        {
           return Optional.ofNullable(student).map(s –> s.getName()).orElse("Unkown");
        }
    

    8、orElseGet()方法

    orElseGet()方法的源码:

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

    orElseGet()方法与orElse()方法类似,区别在于orElseGet()方法的入参为一个Supplier对象,用Supplier对象的get()方法的返回值作为默认值。如:

        public static String getGender(Student student)
        {
            return Optional.ofNullable(student).map(s –> s.getGender()).orElseGet(() -> "Unkown");      
        }
    

    9、orElseThrow()方法

    orElseThrow()方法的源码:

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

    orElseThrow()方法其实与orElseGet()方法非常相似了,入参都是Supplier对象,只不过orElseThrow()的Supplier对象必须返回一个Throwable异常,并在orElseThrow()中将异常抛出:

        public static String getGender1(Student student)
        {
            return Optional.ofNullable(student).map(s –> s.getGender()).orElseThrow(() -> new RuntimeException("Unkown"));      
        }
    

    orElseThrow()方法适用于包装对象值为空时需要抛出特定异常的场景。

    4、总结

    总的来说,Optional类可以帮我们有效的处理null值,但是个人觉得不用函数式编程的话,感觉没什么体验,因为Optional实现的功能,有很多替代方案,if-else、三目等都可以;但Optional是用于函数式的一个整体中的一环,让函数式更流畅。所以如果你使用的是Java8,建议去了解一下。

  • 相关阅读:
    hdu 5101 Select
    hdu 5100 Chessboard
    cf B. I.O.U.
    cf C. Inna and Dima
    cf B. Inna and Nine
    cf C. Counting Kangaroos is Fun
    Radar Installation 贪心
    spfa模板
    Sequence
    棋盘问题
  • 原文地址:https://www.cnblogs.com/tanghaorong/p/11644253.html
Copyright © 2011-2022 走看看