• 了不起的Java-Optional替代null处理


    Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException)。是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。
    在这段代码就可能产生空异常;

    String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
    //需要检查:
    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();
                }
            }
        }
    }

    Optional类的依赖依然还是函数接口那一套东西:

    import java.util.function.Consumer;
    import java.util.function.Function;
    import java.util.function.Predicate;
    import java.util.function.Supplier;

    是要把面向接口编程走到底了。私有字段只有一个:

    private final T value;

    原生代码还是相当简单的:

        public static<T> Optional<T> empty() {
            @SuppressWarnings("unchecked")
            Optional<T> t = (Optional<T>) EMPTY;
            return t;
        }
    
        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);
        }
    
        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));
            }
        }
        ......

    创建 Optional 包装实例

    建空

            Optional<Insurance> emptyOpt = Optional.empty();
            try {
                Insurance obj = emptyOpt.get();
                System.out.println("不为空");
            } catch (NoSuchElementException ex) {
                System.out.println("为空");
            }

    尝试访问 emptyOpt 变量的值会导致 NoSuchElementException,因为原声代码:

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

    建可能为空(ofNullable不抛异常)

    Optional<Insurance> opt = Optional.ofNullable(null);

    不会异常
    如此神奇,原因在于原生代码已经进行了判空,把私有value定成了empty(),这种不抛异常也会产生一个问题,那就是在消费实例的时候,还是要过滤掉空值。

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

    建为空时抛异常(即of不可为空)

        public static void whenCreateOfEmptyOptional_thenNullPointerException() {        
            Insurance ins = new Insurance();        
            Optional<Insurance> opt = Optional.of(ins);
            ins=null;
            try {
                opt = Optional.of(ins);
                System.out.println("不为空");
            } catch (NullPointerException ex) {
                System.out.println("为空");
            }        
        }

    会进入异常。跟踪原生代码发现了原因:

        public static <T> T requireNonNull(T obj) {
            if (obj == null)
                throw new NullPointerException();
            return obj;
        }

    访问 Optional 对象的值

    get

    从 Optional 实例中取回实际值对象的方法之一是使用 get() 方法:

    @Test
    public void whenCreateOfNullableOptional_thenOk() {
    String name = "John";
    Optional<String> opt = Optional.ofNullable(name);
    
    assertEquals("John", opt.get());
    }

    这个方法会在值为 null 的时候抛出异常。要避免异常,你可以选择首先验证是否有值:

    @Test
    public void whenCheckIfPresent_thenOk() {
    User user = new User("john@gmail.com", "1234");
    Optional<User> opt = Optional.ofNullable(user);
    assertTrue(opt.isPresent());
    
    assertEquals(user.getEmail(), opt.get().getEmail());
    }

    ifPresent的使用

    原生代码:

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

    只有当值不为空,才执行消费者方法accept,这是比较安全的。

        public static void printName(Insurance obj)
        {
            Optional.ofNullable(obj).ifPresent(u ->  System.out.println("The name is : " + u.getName()));
        }
    
            Insurance obj = new Insurance();
            obj.setName("张三");
            Insurance objNull = null;        
            printName(obj);
            printName(objNull);
    
    //输出:The name is : 张三

    isPresent的使用

    感觉有了ifPresent,不需要再用isPresent来判断空了,否则感觉非常啰嗦。

    orElse的使用

    在对象为空的时候返回默认值。它的工作方式非常直接,如果有值则返回该值,否则返回传递给它的参数值。

    Insurance obj = new Insurance();
    obj.setName("张三");
    Insurance objNull = null;    
    Insurance result = Optional.ofNullable(objNull).orElse(obj);
    System.out.println(obj.hashCode());
    System.out.println(result.hashCode());

    result实际是返回的obj,而不是null。

    filter的使用

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

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

    map的使用

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

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

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

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

    flatMap的使用

    跟map()方法不同的是,入参Function函数的返回值类型为Optional<U>类型,而不是U类型,这样flatMap()能将一个二维的Optional对象映射成一个一维的对象,
    总而言之,map进去是什么,出来维度不变,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));
            }
        }
    
    //例子
            Optional<String> nonEmptyGender = Optional.of("male");
            Optional<String> emptyGender = Optional.empty();
    
            System.out.println("Non-Empty Optional:: " + nonEmptyGender.map(String::toUpperCase));
            System.out.println("Empty Optional    :: " + emptyGender.map(String::toUpperCase));
    
            Optional<Optional<String>> nonEmptyOtionalGender = Optional.of(Optional.of("male"));
            System.out.println("Optional value   :: " + nonEmptyOtionalGender);
            System.out.println("Optional.map     :: " + nonEmptyOtionalGender.map(gender -> gender.map(String::toUpperCase)));
            System.out.println("Optional.flatMap :: " + nonEmptyOtionalGender.flatMap(gender -> gender.map(String::toUpperCase)));

    封装可能的空值

    使用ofNullable,比如在一些map中的key为空的情况下使用:

    Optional<Insurance> opt = Optional.ofNullable(map.get("someNullKey"));
  • 相关阅读:
    MySQL 4.1x 中文乱码效果
    linux内核中的“捏造化”
    Ubuntu开发者峰会在布拉格举行
    Decode 函数的用法
    Solaris 10拆卸jdk1.6及点窜成默许JDK
    教你编写高机能的mysql语法
    DirectShow9.0在vs2005中存在的问题解决
    Unicode,unicoidebig,Asci,UTF8文件read和write
    自已写了个GDI类,实现了相对路径载入任意类型的图片函数,并加一个在CRECT矩形上贴图的函数(5月25日写)
    两种解析EDIT控件上文本的方式
  • 原文地址:https://www.cnblogs.com/starcrm/p/12410903.html
Copyright © 2020-2023  润新知