Java8新特性Optional类

Java8 Optional 类

前言

在 Java 8 之前,任何访问对象方法或属性的调用都可能导致 NullPointerException;

在这个小示例中,如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查;

if (user != null) {
	Address address = user.getAddress();
	if (address != null) {
		Country country = address.getCountry();
		if (country != null) {
			String code = country.getCountryCode();
			if (code != null) {
				code = code.toUpperCase();
			}
		}
	}
}

这很容易就变得冗长,难以维护。

为了简化这个过程,我们来看看用 Optional 类是怎么做的。从创建和验证实例,到使用其不同的方法,并与其它返回相同类型的方法相结合,下面是见证 Optional 奇迹的时刻。

创建 Optional 实例

​ 重申一下,这个类型的对象可能包含值,也可能为空。你可以使用同名方法创建一个空的 Optional。

@Test
public void createEmptyOptionalTest() {
	// 使用同名方法创建一个空的 Optional
	Optional<User> opt = Optional.empty();
	// 报错 NoSuchElementException
	opt.get();
}

尝试访问 opt 变量的值会导致 NoSuchElementException(线程访问越界异常)。

可以使用 of() 和 ofNullable() 方法创建包含值的 Optional。两个方法的不同之处在于如果你把 null 值作为参数传递进去,of() 方法会抛出 NullPointerException:

@Test
public void createOfEmptyOptionalTest() {
	User user = null;
	// 把 null 值作为参数传递进去,of() 方法会抛出 NullPointerException
	Optional<User> opt = Optional.of(user);
	// 使用 ofNullable() 方法,对象即可能是 null 也可能是非 null
	Optional<User> opt1 = Optional.ofNullable(user);
}

把 null 值作为参数传递进去,使用 of() 方法会抛出 NullPointerException,而使用 ofNullable() 方法,则返回一个空的 Optional 类。
因此,应该明确对象不为 null 的时候使用 of(),如果对象即可能是 null 也可能是非 null,你就应该使用 ofNullable() 方法。

Optional API常用接口的用法

get()方法

@Test
public void getTest() {
	String name = "hhh";
	Optional<String> opt = Optional.ofNullable(name);
	System.out.println(opt.get());
}

·控制台输出
hhh

可以看到,get()方法主要用于返回包装对象的实际值,但是如果包装对象值为null,会抛出NoSuchElementException异常。

isPresent()方法

@Test
public void isPresentTest() {
	User user = new User("hhh", "1234");
	Optional<User> opt = Optional.ofNullable(user);
	System.out.println(opt.isPresent());
	if (opt.isPresent()) {
		System.out.println(opt.get());
	}
}

·控制台输出
true
User(code=hhh, name=1234, address=null)

isPresent()方法用于判断包装对象的值是否非空,但是这种用法不但没有减少null的防御性检查,而且增加了Optional包装的过程

isPresent()方法

@Test
public void ifPresentTest() {
	User user = new User("hhh", "1234");
	Optional<User> opt = Optional.ofNullable(user);
	opt.ifPresent(u -> System.out.println(u));
}

·控制台输出
User(code=hhh, name=1234, address=null)

ifPresent() 方法除了执行检查,还接受一个Consumer(消费者) 参数,如果对象不是空的,就对执行传入的 Lambda 表达式

filter()方法

@Test
public void filterTest() {
	User user = new User("ccc", "1234");
	Optional<User> result = Optional.ofNullable(user).filter(u -> u.getCode() != null && u.getCode().contains("@"));
	System.out.println(result);
}

·控制台输出
Optional[User(code=ccc, name=1234, address=null)]

filter() 接受一个 Predicate 参数,返回测试结果为 true 的值。如果测试结果为 false,会返回一个空的 Optional

map()方法

@Test
public void mapTest() {
	User user = new User("ccc", "1234");
	Optional<String> code = Optional.ofNullable(user).map(u -> u.getCode());
	System.out.println(code);
}

·控制台输出
Optional[ccc]

map()方法的参数为Function(函数式接口)对象,map()方法将Optional中的包装对象用Function函数进行运算,并包装成新的Optional对象

flatMap()方法

public void flatMapTest() {
	User user = new User("ccc", "1234");
	Optional<String> code = Optional.ofNullable(user).flatMap(u -> Optional.ofNullable(u.getCode()));
	System.out.println(code);
}

·控制台输出
Optional[anna@gmail.com]

跟map()方法不同的是,入参Function函数的返回值类型为Optional类型,而不是U类型,这样flatMap()能将一个二维的Optional对象映射成一个一维的对象

orElse()方法

private User createNewUser() {
	System.out.println("Create New User");
	return new User("create", "1234");
}

@Test
public void orElseTest() {
	User user = new User("hhh", "1234");
	User result = Optional.ofNullable(user).orElse(createNewUser());
	System.out.println(result);
	User user1 = null;
	User result1 = Optional.ofNullable(user1).orElse(createNewUser());
	System.out.println(result1);
}

·控制台输出
Create New User
User(code=hhh, name=1234, address=null)
Create New User
User(code=hhh, name=1234, address=null)

orElse()方法,不论对象User是否为空值都会调用createNewUser()方法创建User对象

orElseGet()方法

private User createNewUser() {
	System.out.println("Create New User");
	return new User("create", "1234");
}

@Test
public void orElseGetTest() {
	User user = new User("hhh", "1234");
	User result = Optional.ofNullable(user).orElseGet(()->createNewUser());
	System.out.println(result);
	User user1 = null;
	User result1 = Optional.ofNullable(user1).orElseGet(()->createNewUser());
	System.out.println(result1);
}

·控制台输出
User(code=hhh, name=1234, address=null)
Create New User
User(code=hhh, name=1234, address=null)

orElseGet()方法与orElse()方法不同,User对象非空值时,方法会返回对应的对象,对象为空值时,才调用createNewUser()方法创建User对象

orElseThrow()方法

@Test
public void orElseThrowTest() {
	User user = new User("hhh", "1234");
	User result = Optional.ofNullable(user).orElseThrow( () -> new IllegalArgumentException());
	System.out.println(result);
}

·控制台输出
User(code=hhh, name=1234, address=null)

orElseThrow()的Supplier对象必须返回一个Throwable异常,并在orElseThrow()中将异常抛出,orElseThrow()方法适用于包装对象值为空时需要抛出特定异常的场景

Optional 类的链式方法

为了更充分的使用 Optional,可以链接组合其大部分方法,因为它们都返回相同类似的对象
首先,重构类,使其 getter 方法返回 Optional 引用

public class User {
	private String code;
	private String name;
	private Address address;
	public Optional<Address> getAddress(){
		return Optional.ofNullable(address);
	}
}

public class Address {
	private Country country;
	public Optional<Country> getCountry(){
		return Optional.ofNullable(country);
	}
}

现在可以删除 null 检查,替换为 Optional 的方法

public void chainingTest() {
	User user = new User("hhh", "1234");
	String result = Optional.ofNullable(user)
		.flatMap(u -> u.getAddress())
		.flatMap(a -> a.getCountry())
		.map(c -> c.getCountryCode())
		.orElse("default");
	System.out.println(result);
}

·控制台输出
default

上面的代码可以通过方法引用进一步缩减

public void chainingTest() {
	User user = new User("hhh", "1234");
	String result = Optional.ofNullable(user)
		.flatMap(User::getAddress())
		.flatMap(Address::getCountry())
		.map(Country::getCountryCode())
		.orElse("default");
	System.out.println(result);
}

·控制台输出
default

结果现在的代码看起来比之前采用条件分支的冗长代码简洁多了

注意事项

使用Optional开发时要注意正确使用Optional的“姿势”,特别注意不要使用3.2节提到的错误示范,谨慎使用isPresent()和get()方法,尽量多使用map()、filter()、orElse()等方法来发挥Optional的作用

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值