欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java8新特性Optional类

程序员文章站 2022-03-04 10:50:02
...

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 (isocode != null) {
				code = code.toUpperCase();
			}
		}
	}
}

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

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

创建 Optional 实例

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

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

尝试访问 emptyOpt 变量的值会导致 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()方法

Instant标识某个时间(类似Date),精确到纳秒(不像Date到毫秒),因为精确到纳秒,所以用一位Long类型是不够的的,所以实际是有两个Long字段组成,第一部分保存的是子1970年1月1日开始到现在的秒数,滴而部分保存的是纳秒数;

@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("[email protected]", "1234");
	Optional<User> opt = Optional.ofNullable(user);
	System.out.println(opt.isPresent());
	if (opt.isPresent()) {
		System.out.println(opt.get());
	}
}

·控制台输出
true
User(code=hhh@email.com, name=1234, address=Optional.empty)

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

isPresent()方法

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

·控制台输出
User(code=hhh@email.com, name=1234, address=Optional.empty)

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

filter()方法

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

·控制台输出
Optional[User(code=ccc@email.com, name=1234, address=Optional.empty)]

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

map()方法

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

·控制台输出
Optional[ccc@email.com]

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

flatMap()方法

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

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

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

orElse()方法

private User createNewUser() {
	System.out.println("Create New User");
	return new User("[email protected]", "1234");
}

@Test
public void orElseTest() {
	User user = new User("[email protected]", "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@email.com, name=1234, address=Optional.empty)
Create New User
User(code=hhh@email.com, name=1234, address=Optional.empty)

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

orElseGet()方法

private User createNewUser() {
	System.out.println("Create New User");
	return new User("[email protected]", "1234");
}

@Test
public void orElseGetTest() {
	User user = new User("[email protected]", "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@email.com, name=1234, address=Optional.empty)
Create New User
User(code=hhh@email.com, name=1234, address=Optional.empty)

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

orElseThrow()方法

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

·控制台输出
User(code=hhh@email.com, name=1234, address=Optional.empty)

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("[email protected]", "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("[email protected]", "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的作用

相关标签: Java java