在写程序时,经常需要对创建的对象或属性进行null值判断,但是有时可能会疏忽没有对null进行判断,就会引发空指针问题,null值在程序设计语言中,是为了表示变量值的缺失;

          java8中引入了Optional<T>,可以表示值的存在与不存在(null),对存在或不存在的变量值进行建模,并且可以避免空指针异常.

以下实例演示可以说明Optional<T>可以很好解决类型T为null时的问题。

package com.xiaomifeng1010.rbacboot.common.util;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.junit.Test;

import java.util.Optional;
import java.util.function.Supplier;

/**
 * @author xiaomifeng1010
 * @version 1.0
 * @date: 2020/3/21 16:18
 */

public class Java8Optional {
    /**
     * 在java8之前如果要保存Info对象,需要做多层非空检查,多层if嵌套
     * @param info
     */
//    public void savaInfo(Info info){
//        if (info !=null){
//            if (info.getAddress() != null) {
                则保存info
//            }
//        }
//
//    }

    /**
     * 过多的退出语句
     * @param info
     */
//    public void savaInfo2(Info info){
//        if (info==null){
//            return;
//        }
//        if (info.getAddress()==null){
//            return;
//        }
//    }

//    Optional可以解决判断null问题

    /**
     * 创建Optional对象
     */
    public void createOptional(){
        //    创建一个空白的Optional
        Optional<Address> optionalAddress=Optional.empty();
//   依据一个非空值创建Optional(new Address()不能为null)
        Optional<Address> optionalAddress2=Optional.of(new Address());
//    可接受null值创建Optional(当new Address()为null时,调用empty()方法,创建一个空白Optional对象)
        Optional<Address> getOptionalAddress3= Optional.ofNullable(new Address());

    }

    /**
     * 使用map(同Stream API中的中间操作map)从optional对象中提取和转换值
     */
    public void mapToOptional(){
        Optional<Address> addressOptional=Optional.ofNullable(new Address("南京路","20号"));
//        直接从Optional<Address>中获取Address类中street属性
        Optional<String> street=addressOptional.map(Address::getStreet);

    }

    public void flatMapToOptional(){
        Info info=new Info();
        Optional<Info> infoOptional=Optional.of(info);
//        再使用map就会报错,因为map转换输出的还是一个optional对象,会造成原本的optional里面存放optional对象
//        infoOptional.map(info -> info.getOptionalAddress());
//        可以使用flatMap方法从Info类中获取Optional<Address>,然后获取Address类型的street属性
        Optional<String> stringOptional=infoOptional.flatMap(Info::getOptionalAddress).map(Address::getStreet);
        System.out.println(stringOptional);
    }

    /**
     * 默认行为及解引用 optional对象
     */
    @Test
    public void defaultValue(){
        Optional<Address> addressOptional= Optional.ofNullable(null);
//        获取街道名,如果无,则设置为"上海路"
        String street = addressOptional.map(Address::getStreet).orElse("上海路");
        System.out.println(street);

    }

    public static void main(String[] args) {
        Info info=new Info();
        info.setUsername("xiaomifeng1010");
        info.setPassword("123456");
        info.setAge(30);
        info.setOptionalAddress(Optional.of(new Address("广州路","12号")));
        boolean present=info.getOptionalAddress().filter(address -> address.
getDoor().contains("23"))
//      isPresent():如果存在值,则为true.不存在值,则为false
                .isPresent();
        System.out.println(present);


//        address为null,过滤会报错,需要抛出异常,并处理
        info.setOptionalAddress(Optional.empty());
        Address address=null;
        try {
           address= info.getOptionalAddress().filter(address1 -> address1.getDoor().contains("34"))
.orElseThrow(new Supplier<Throwable>() {
                @Override
                public Throwable get() {
                    return new Exception("哎呀,出错了");
                }
            });
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }

//          Info类中的年龄为30,不满足大于50,所以不会找到地址信息,输出“不存在”
        System.out.println(getSteet(Optional.of(info),50));

    }

    public static String getSteet(Optional<Info> info,int minAge){
        return info.filter(information -> information.getAge()>= minAge).
flatMap(Info::getOptionalAddress)
                .map(Address::getStreet).orElse("不存在");

    }



}

/**
 * 自定义一个类Info
 */
@Data
class Info{
    private String username;
    private String password;
    private Integer age;
//    private Address address;
/**
 * 为了解决空指针问题(即Address可能为null的情况),创建Optional对象,当Address未赋值初始化,
 * 会生成一个empty(空白)的Optional对象,而不会产生null(可以允许Address对象缺失)
 *
  **/
    Optional<Address> optionalAddress;

}

/**
 * 住址对象
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
class Address{
    /**
     * 街道
     */
    private String street;
    /**
     * 门牌
     */
    private String door;

}

Optional<T>对应的常用方法:

方法

描述

empty

返回一个空的 Optional 实例

of

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

ofNullable

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

map(Function<? super T, ? extends U> mapper)

如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()

flatMap(Function<? super T, Optional<U>> mapper)

与map 类似,要求返回值必须是Optional

filter

如果值存在并且满足提供的断言, 就返回包含该值的 Optional 对象;否则返回一个空的 Optional 对象

get

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

public void ifPresent(Consumer<? super T> consumer)

如果值存在,就执行使用该值的方法调用,否则什么也不做

public boolean isPresent()

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

orElse

如果有值则将其返回,否则返回一个默认值(即orElse中设置的值

orElseGet(Supplier<? extends T> other)

如果有值则将其返回,否则返回一个由指定的 Supplier 接口生成的值

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X

如果有值则将其返回,否则抛出一个由指定的 Supplier 接口生成的异常