重学JDK8新特性之Optional

32 阅读3分钟

以前对NULL的处理

//String userName = "张三";
String userName = null;
if(userName != null){
	System.out.println("字符串的长度:" + userName.length());
}else{
	System.out.println("字符串为空");
}

Optional类

Optional是一个没有子类的工具类,Optional是一个可以为null的容器对象,它的主要作用就是为了避 免Null检查,防止NullpointerException

Optional的基本使用

// 第一种方式 通过of方法 of方法是不支持null的
Optional<String> op1 = Optional.of("zhangsan");
//Optional<Object> op2 = Optional.of(null);
// 第二种方式通过 ofNullable方法 支持null
Optional<String> op3 = Optional.ofNullable("lisi");
Optional<Object> op4 = Optional.ofNullable(null);
// 第三种方式 通过empty方法直接创建一个空的Optional对象
Optional<Object> op5 = Optional.empty();

Optional的常用方法

/**
* Optional中的常用方法介绍
* get(): 如果Optional有值则返回,否则抛出NoSuchElementException异常
* get()通常和isPresent方法一块使用
* isPresent():判断是否包含值,包含值返回true,不包含值返回false
* orElse(T t):如果调用对象包含值,就返回该值,否则返回t
* orElseGet(Supplier s):如果调用对象包含值,就返回该值,否则返回 Lambda表达式的返
回值
*/
Optional<String> op1 = Optional.of("zhangsan");
Optional<String> op2 = Optional.empty();
// 获取Optional中的值
if(op1.isPresent()){
	String s1 = op1.get();
	System.out.println("用户名称:" +s1);
}
if(op2.isPresent()){
	System.out.println(op2.get());
}else{
	System.out.println("op2是一个空Optional对象");
}
String s3 = op1.orElse("李四");
System.out.println(s3);
String s4 = op2.orElse("王五");
System.out.println(s4);
String s5 = op2.orElseGet(()->{
	return "Hello";
});
System.out.println(s5);

Person p = new Person("zhangsan",18);
Optional<Person> op = Optional.of(p);
String name = getNameForOptional(op);
System.out.println("name="+name);

public String getNameForOptional(Optional<Person> op){
	if(op.isPresent()){
		String msg = //op.map(p -> p.getName())
			op.map(Person::getName)
			//.map(p -> p.toUpperCase())
			.map(String::toUpperCase)
			.orElse("空值");
		return msg;
	}
return null;
}

额外补充三个常用的:

获取Optional容器的对象:

  • T orElseThrow(Supplier<? extends X> exceptionSupplier) :如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。

过滤:

  • Optional<T> filter(Predicate<? super <T> predicate):如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。

映射

  • <U>Optional<U> map(Function<? super T,? extends U> mapper):如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。

Optional实战

这部分是通过查阅实际上开发中写法总结出来的,并不是只介绍了Optional的概念,而是融入了实际开发中考虑到的情况,实际上在开发中空指针异常的异常很常见,而灵活的的使用Optional能够很好的避免这种情况。

场景一:

PatientInfo patientInfo = patientInfoDao.getPatientInfoById(consultOrder.getPatientId());
if (patientInfo != null) {
    consultInfoResp.setPatientHead(patientInfo.getHead());
}

// 使用Optional 和函数式编程,一行搞定,而且像说话一样
Optional.ofNullable(patientInfo).ifPresent(p -> consultInfoResp.setPatientHead(p.getHead()));

场景二:

public void test1() throws Exception {
    Student student = new Student(null, 3);
    if (student == null || isEmpty(student.getName())) {
        throw new Exception();
    }
    String name = student.getName();
    // 业务省略...

    // 使用Optional改造
    Optional.ofNullable(student).filter(s -> !isEmpty(s.getName())).orElseThrow(() -> new Exception());
}

public static boolean isEmpty(CharSequence str) {
    return str == null || str.length() == 0;
}

场景三:

public static String getChampionName(Competition comp) throws IllegalArgumentException {
    if (comp != null) {
        CompResult result = comp.getResult();
        if (result != null) {
            User champion = result.getChampion();
            if (champion != null) {
                return champion.getName();
            }
        }
    }
    throw new IllegalArgumentException("The value of param comp isn't available.");
}

这个在开发中是很常见的一种逻辑。去判读传进来的参数时候为空,或者是从数据库中获取的对象。由于某些原因,我们不能很流程的直接这样写。

comp.getResult().getChampion().getName()

上面的写法用Optional改写:

public static String getChampionName(Competition comp) throws IllegalArgumentException {
    return Optional.ofNullable(comp)
            .map(Competition::getResult)  // 相当于c -> c.getResult(),下同
            .map(CompResult::getChampion)
            .map(User::getName)
            .orElseThrow(()->new IllegalArgumentException("The value of param comp isn't available."));
}

场景四:

int timeout = Optional.ofNullable(redisProperties.getTimeout())
					  .map(x -> Long.valueOf(x.toMillis()).intValue())
					  .orElse(10000);