Java8新特性(三):Optional类

标签: Java  

Optional 是 JDK1.8 中的一个很流弊的新特性,每个接触JAVA的都遇到过 NPE(NullPointerException),1.8中Optional 帮助我们很好的避免了这一现象。

of/empty/ofNullable

User user = null; // user 是一个 空对象	

/**
 * 源码:        
 * public static <T> Optional<T> of(T value) { // of会创建一个 实例化一个 Optional
 *     return new Optional<>(value);
 * }
 *
 * private Optional(T value) { // 检查value是否为null
 *	   this.value = Objects.requireNonNull(value);
 * }
 *
 * public static <T> T requireNonNull(T obj) { // 不做解释了...
 *     if (obj == null)
 *         throw new NullPointerException();
 *     return obj;
 * }
 *
 * 解释:如果对象为Null使用of会抛出java.lang.NullPointerException
 */
Optional<User> op1 = Optional.of(user);

Optional<User> op2 = Optional.empty(); // 看API就知道啥意思了,基本没什么太大卵用

/**
 * 源码:        
 * public static <T> Optional<T> ofNullable(T value) {
 *     return value == null ? empty() : of(value);
 * }
 * 解释:如果对象为空就返回 empty() 相反就  调用 of(value); 
 */
Optional<User> op3 = Optional.ofNullable(user);

get/isPresent

Optional<User> op = Optional.ofNullable(null); // 创建一个对象为Null的Optional
    
/**
 * 源码:        
 * public boolean isPresent() {
 *     return value != null;
 * }
 * 解释:因为 value == null 源码是 value != null 因此返回false
 */
System.out.println(op.isPresent());

/**
 * 源码:        
 *  public T get() {
 *     if (value == null) {
 *         throw new NoSuchElementException("No value present");
 *     }
 *     return value;
 * }
 * 解释:如果 value 为空 null 抛出 NoSuchElementException 否则返回 T
 */
System.out.println(op.get());

orElse/orElseGet/orElseThrow

User user = null;
	
/**
 * 源码:        
 * public T orElseGet(Supplier<? extends T> other) {
 * 		return value != null ? value : other.get();
 * }
 * 解释:对象为空返回null,否则返回对象
 * 注:功能与orElse(T other)类似,不过该方法可选值的获取不是通过参数直接获取,而是通过调用传入的Lambda表达式获取
 */
User u1 = Optional.ofNullable(user).orElseGet(User::new);
	
System.out.println(u1);//User{id=null, name='null', email='null', age=null}
	
/**
* 源码:    
 * public T orElse(T other) {
 * 		return value != null ? value : other;
 * }
 * 解释:如果 T 为null则返回一个默认值
 */		
User u2 = (User) Optional.ofNullable(user).orElse(new User(1L, "lisi", "xxx@qq.com", 22));
System.out.println(u2);//Student{id=1, name='lisi', email='xxx@qq.com', age=22}

/**
 * 源码:    
 * public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier)   throws X {
 * 		if (value != null) {
 * 		    return value;
 * 		} else {
 * 		    throw exceptionSupplier.get();
 * 		}
 * }
 * 解释:如果T为null那么抛出异常(本宝宝非常喜欢)
 */			
User u3 = Optional.ofNullable(user).orElseThrow(() -> new RuntimeException("哈哈,报错了吧"));
System.out.println(u3); //抛出:java.lang.RuntimeException: 哈哈,报错了吧

filter/map

// 可以加if判断条件过滤
String filter = Optional.ofNullable("aadasdsa").filter((value) -> value.length() > 5).get();
System.out.println(filter);

// 大小写转换等等..
String map = Optional.ofNullable("aadasdsa").map(String::toUpperCase).get();
System.out.println(map);

使用map函数返回对象,使用flatMap返回Optional

Optional转集合

将一个Optional转为List或者Set

public static <T> List<T> toList(Optional<T> option) {
    return option.
            map(Collections::singletonList).
            orElse(Collections.emptyList());
}

或者更传统的写法:

public static <T> List<T> toList(Optional<T> option) {
    if (option.isPresent())
        return Collections.singletonList(option.get());
    else
        return Collections.emptyList();
}

但是在java8里,其实只需要这么写:

import static java.util.stream.Collectors.*;
//转为List
List<String> list = collect(opt, toList());
//转为Set
Set<String> set  = collect(opt, toSet());

如何正确使用Optional

假设你试图使用Optional来避免可能出现的NullPointerException异常,编写了如下代码:

Optional<User> userOpt = Optional.ofNullable(user);
if (userOpt.isPresent()) {
    User user = userOpt.get();
    // do something...
} else {
    // do something...
}

坦白说,上面的代码与我们之前的使用if语句判断空值没有任何区别,没有起到Optional的正真作用:

if (user != null) {
    // do something...
} else {
    // do something...
}

当我们从之前版本切换到Java 8的时候,不应该还按照之前的思维方式处理null值,Java 8提倡函数式编程,新增的许多API都可以用函数式编程表示,Optional类也是其中之一。这里有几条关于Optional使用的建议:

  1. 尽量避免在程序中直接调用Optional对象的get()isPresent()方法;
  2. 避免使用Optional类型声明实体类的属性;

第一条建议中直接调用get()方法是很危险的做法,如果Optional的值为空,那么毫无疑问会抛出NullPointerException异常,而为了调用get()方法而使用isPresent()方法作为空值检查,这种做法与传统的用if语句块做空值检查没有任何区别。

第二条建议避免使用Optional作为实体类的属性,它在设计的时候就没有考虑过用来作为类的属性,如果你查看Optional的源代码,你会发现它没有实现java.io.Serializable接口,这在某些情况下是很重要的(比如你的项目中使用了某些序列化框架),使用了Optional作为实体类的属性,意味着他们不能被序列化。

下面我们通过一些例子讲解Optional的正确用法:

正确创建Optional对象

上面提到创建Optional对象有三个方法,empty()方法比较简单,没什么特别要说明的。主要是of()ofNullable()方法。当你很确定一个对象不可能为null的时候,应该使用of()方法,否则,尽可能使用ofNullable()方法,比如:

public static void method(Role role) {
    // 当Optional的值通过常量获得或者通过关键字new初始化,可以直接使用of()方法
    Optional<String> strOpt = Optional.of("Hello World");
    Optional<User> userOpt = Optional.of(new User());

    // 方法参数中role值不确定是否为null,使用ofNullable()方法创建
    Optional<Role> roleOpt = Optional.ofNullable(role);
}

orElse()方法的使用

return str != null ? str : "Hello World"

上面的代码表示判断字符串str是否为空,不为空就返回,否则,返回一个常量。使用Optional类可以表示为:

return strOpt.orElse("Hello World")

简化if-else

User user = ...
if (user != null) {
    String userName = user.getUserName();
    if (userName != null) {
        return userName.toUpperCase();
    } else {
        return null;
    }
} else {
    return null;
}

上面的代码可以简化成:

User user = ...
Optional<User> userOpt = Optional.ofNullable(user);

return user.map(User::getUserName)
            .map(String::toUpperCase)
            .orElse(null);

总结一下,新的Optional类让我们可以以函数式编程的方式处理null值,抛弃了Java 8之前需要嵌套大量if-else代码块,使代码可读性有了很大的提高。

「真诚赞赏,手留余香」

请我喝杯咖啡?

使用微信扫描二维码完成支付

相关文章