一起来学Java8(六)——Optional

原创
2019/10/25 14:17
阅读数 724

Optional类是Java8新增的一个类,其作用可以有效避免空指针异常。

Optional类的代码比较简单,很容易就能看懂,其实质就是一个辅助类,把需要处理的对象做了一层包装,然后再使用Optional中的方法时,可以有效得判断处理对象是否为空,从而做出正确判断。

接下来我们看下如何使用Optional。

创建Optional

创建Optional有3种方式:

  • Optional.empty() 返回一个空的Optional
  • Optional.of(不为null的对象)
  • Optional.ofNullable(可以为null的对象)

如果能够确保入参一定不为null,可以用Optional.of,因为在Optional.of内部会判断是否为null,如果是null则抛出异常。

如果不太确定入参是否为null,可以用Optional.ofNullable

对象创建好了,接下来看看如何使用。

isPresent和ifPresent

isPresent()方法判断处理对象是否为null,不为null返回true,源码如下:

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

ifPresent方法有一个入参ifPresent(Consumer<? super T> consumer),它的意思是如果对象不为null,则运行consumer进行处理,有点类似回调函数。

String s = "hello";		
Optional<String> optional = Optional.of(s);
if(optional.isPresent()) {
	System.out.println("the value is " + optional.get());
}

同样可以写成:

optional.ifPresent((val) -> {
	System.out.println("the value is " + val);
});

filter

filter是对处理对象进行判断,如果判断为true,则返回当前Optional,如果为false则返回一个空的Optional对象,其源码如下:

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

filter方法有个参数:Predicate,这是一个函数式接口,因此我们可以使用Lambda表达式来处理。

String s = "hello";		
Optional<String> optional = Optional.of(s);

boolean exist = optional
	.filter(val -> "hello1".equals(val))
	.isPresent();
System.out.println(exist); // false

map和flatMap

map方法的作用可以简单理解成从处理对象中取出其它对象,然后返回一个新的Optional。如下代码所示:

public class OptionalMapTest {
	static class Goods {
		private String goodsName;
		private Company company;

	    ...getter setter
	}

	static class Company {
		private String companyName;

		...getter setter
	}

	public static void main(String[] args) {
		Company company = new Company();
		company.setCompanyName("Apple");
		Goods goods = new Goods();
		goods.setGoodsName("iphoneX");
		goods.setCompany(company);
		
		Optional<Goods> optional = Optional.of(goods);
		String companyName = optional
				// 从goods中取出Company,返回一个新的Optional<Company>
				.map(goodsObj -> goodsObj.getCompany())
				// 从company中取出companyName,返回一个新的Optional<String>
				.map(companyObj -> companyObj.getCompanyName())
				// 得到companyName
				.get();
		System.out.println(companyName);
	}
	
}

什么情况下该使用flatMap呢,我们把Goods中的的Company对象改成Optional<Company>

static class Goods {
	private String goodsName;
	private Optional<Company> company;

	...getter setter
}

此时下面这段代码会编译报错

String companyName = optional
	// 从goods中取出Company,返回一个新的Optional<Company>
	.map(goodsObj -> goodsObj.getCompany()) // !!这里会报错
	// 从company中取出companyName,返回一个新的Optional<String>
	.map(companyObj -> companyObj.getCompanyName())
	// 得到companyName
	.get();

主要是这行代码optional.map(goodsObj -> goodsObj.getCompany())。因为此时返回的是一个Optional<Optional<Company>>对象。

而我们需要的是Optional<Company>对象,这个时候就应该用到flatMap了,只要把optional.map(goodsObj -> goodsObj.getCompany())改成optional.flatMap(goodsObj -> goodsObj.getCompany())即可。

String companyName = optional
	// 从goods中取出Company,返回一个新的Optional<Company>
	.flatMap(goodsObj -> goodsObj.getCompany())
	// 从company中取出companyName,返回一个新的Optional<String>
	.map(companyObj -> companyObj.getCompanyName())
	// 得到companyName
	.get();

简单的理解就是:

  • optional.map() 会把返回的结果再次放到一个Optional中
  • optional.flatMap() 不会把结果放入放到Optional中,把这个操作交给开发者来处理,让开发者自己返回一个Optional

get,orElse,orElseGet,orElseThrow

  • get():返回被处理的值,如果值为空,则抛出异常
String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.get()); // 抛出java.util.NoSuchElementException: No value present

针对这种情况,有几种处理方式

方式1:使用isPresent()

String s = null;
Optional<String> optional = Optional.ofNullable(s);
if (optional.isPresent()) {
    System.out.println(optional.get());
} else {
    System.out.println("默认值");
}

方式2:使用orElse(默认值)

String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElse("默认值"));

orElse(默认值)的意思是如果Optional中的值为null,则返回给定的默认值。

方式3:使用orElseGet(Supplier)

String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElseGet(() -> "默认值"));

orElse(Supplier)的意思是如果Optional中的值为null,则执行指定的Supplier接口,由于Supplier是个函数式接口,因此可以使用Lambda表达式代替。

由此看来,方式2和方式3的处理是比较优雅的。

方式2和方式3的区别在于,方式3可以延迟返回,只有值为null的情况下才会触发() -> "默认值",从而避免生成无用对象,方式2不管如何都生成了"默认值"这个字符串对象。下面的例子可以说明:

String s = "1";
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElse(getDefault()));

打印:

生成了字符串对象
1

即使Optional中的值不为null,但还是执行了getDefault(),这完全没必要,再来看下使用orElseGet

String s = "1";
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElseGet(() -> getDefault()));

打印:1

接着再看下orElseThrow,如果值为null,则直接抛出异常

String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElseThrow(() -> new NullPointerException("不能为空")));

Optional实战

{
	"user": {
		"age": 20
		,"name": "Jim"
		,"address": {
			"province": "浙江省"
			,"postcode": "111111"
		}
   }
}

假设有这样一个json字符串,现在要获取postcode信息。如果不用Optional的话,要写各种if…else语句,还要判断字段是否存在。

String postcode = "unknown";
JSONObject user = jsonObj.getJSONObject("user");
if (user != null) {
    JSONObject address = user.getJSONObject("address");
    if (address != null) {
        String code = address.getString("postcode");
        if (postcode != null) {
            postcode = code;
        }
    }
}
System.out.println(postcode);

但是用Optional可以这样写:

JSONObject jsonObj = JSON.parseObject(json);

String postcode = Optional.ofNullable(jsonObj)
        .flatMap(jsonObject -> Optional.ofNullable(jsonObject.getJSONObject("user")))
        .flatMap(jsonObject -> Optional.ofNullable(jsonObject.getJSONObject("address")))
        .flatMap(jsonObject -> Optional.ofNullable(jsonObject.getString("postcode")))
        .orElse("unknown");

System.out.println(postcode);

注意,这里要使用flatMap,由开发者手动返回一个Optional对象,如果使用map的话则返回Optional<Optional<JSONObject>>

最后一句.orElse("unknown")表示如果一路走下来没有找到值,则返回一个默认值。

Optional的优势是处理嵌套数据结构,如这里的json数据。假如这段json数据结构不是完整的,postcode字段不存在,或者整个address字段都不存在,在无法保证嵌套数据中的值是否存在的情况下,使用Optional是个不错的选择。它都能确保有个正确的返回值。

小节

本篇主要介绍了Optional类的用法,同时演示了如何使用Optional处理嵌套数据。

定期分享技术干货,一起学习,一起进步!

展开阅读全文
加载中

作者的其它热门文章

打赏
0
4 收藏
分享
打赏
0 评论
4 收藏
0
分享
返回顶部
顶部