原文地址html
Java程序员面临的最多见异常之一是NullPointerException。 JVM会在运行时将该异常做为运行时异常抛出。java
众所周知,当程序须要一个对象可是却找到一个null
值时,就会抛出NullPointerException。对空指针异常进行处理是Java程序员最容易忽略的场景之一。程序员
在继续执行常规业务逻辑以前,须要在应用程序中处理空指针异常,以免在运行时产生空指针异常。这致使了没必要要的空指针检测代码的存在。数据库
为了在Java中处理这些用于进行空指针检测的模板代码,因此Java 8中引入了新类型Optional<T>
。api
据Oracle说,Java 8中引入的Optional做为一个容器类型,用于包装值不存在或为null的状况。 Optional是
java.util
包中一个具备final
修饰符的类。oracle
咱们来看看没有Optional会遇到哪些问题。app
假设在咱们的程序中具备如下方法。此方法使用员工的ID
从数据库中检索员工的详细信息:ide
Employee findEmployee(String id) {
...
};
复制代码
假设在使用上述方法时所提供的ID
在数据库中不存在。而后,该方法将返回null
值。如今,若是咱们已经编写了下面的代码:函数
Employee employee = findEmployee("1234");
System.out.println("Employee's Name = " + employee.getName());
复制代码
上面的代码将在运行时抛出NullPointerException异常,由于程序员在使用该值以前没有对它进行空指针检测。学习
如今,让咱们看看Java 8引入的Optional类型将如何解决上述问题并帮助消除NullPointerException
异常。
如下是上述代码修改事后的代码:
Optional<Employee> findEmployee(String id) {
...
};
复制代码
在上面的代码中,咱们将返回类型修改成Optional<Employee>
来向调用者代表与给定ID
的对应的员工可能不存在。
如今,须要在调用方中明确表达这一事实。
调用者应该编写以下代码:
Optional <Employee> optional = findEmployee("1234");
optional.ifPresent(employee -> {
System.out.println("Employee name is " + employee.getName());
})
复制代码
你能够看到咱们在上述代码的第一行中建立了一个Optional对象。如今,咱们可使用Optional对象上面的各类方法。
以上代码段中的ifPresent()
方法仅在员工存在的状况下才调用提供的lambda表达式。 若是员工不存在,则什么事都不会作。
如下列出的是使用Optional的一些优势:
Methods Description public static Optional empty() This method returns an empty Optional object. No value is present for this Optional. public static Optional of(T value) This method returns an Optional with the specified value that is not null. public static Optional ofNullable(T value) This method returns an Optional describing the specified value if the value is non-null; otherwise, it returns an empty Optional. public T get() If a value is present in this Optional, then it returns the value. Otherwise, it throws NoSuchElementException. public boolean isPresent() This method returns a true value if there is a value present. Otherwise, it returns false. public void ifPresent(Consumer<? super T> consumer) If a value is present, then the consumer with the provided value is invoked. Otherwise, it does nothing. public Optional filter(Predicate<? super T> predicate) If a value is present and it also matches the given predicate, then it returns an Optional describing the value. Otherwise, it returns an empty Optional. public Optional map(Function<? super T,? extends U> mapper) If a value is present, then the mapping function is applied, and if the result is not a null value, then it returns an Optional describing the result. Otherwise, it returns an empty Optional. public Optional flatMap(Function<? super T,Optional mapper) If the value is present, then it applies the provided Optional-bearing mapping function to it and it returns that result. Otherwise, it returns an empty Optional. public T orElse(T other) This method returns the value if present; otherwise, it returns other. public T orElseGet(Supplier<? extends T> other) This method returns the value if present. Otherwise, it invokes other and returns the result of the invocation. public T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X extends Throwable If the value is present, this method returns the contained value. Otherwise, it throws an exception to be created by the provided supplier. public boolean equals(Object obj) This method is used for indicating whether some other object is “equal to” this Optional or not. public int hashCode() This method returns the hash code value of the present value if it exists. Otherwise, it returns 0 (zero) . public String toString() This method is simply used to return a non-empty string representation of the Optional, which is suitable for debugging.
在本节中,咱们将研究用于建立Optional对象的方法:
1. 建立一个包含空值的Optional对象
下面的代码演示了如何建立一个具备空值的Optional对象。它用来表示值为null
的状况。
Optional <Employee> employee = Optional.empty();
复制代码
2. 建立一个具备非空值的Optional对象
下面的代码展现了如何建立一个具备非空值的Optional对象。
Employee employee = new Employee("1234", "TechBlogStation");
Optional <Employee> optional = Optional.of(employee);
复制代码
请注意,若是为Optional.of()
的参数提供了null,则它将当即抛出NullPointerException异常,因此建立Optional对象将会失败。
如今,让咱们学习经过不一样的方法检查Optional对象中是否存在非空值:
1. isPresent()方法
若是Optional对象中存在非空值,则方法isPresent()
返回true,不然,它返回false。
if (optional.isPresent()) {
// optional中有值
System.out.println("Value - " + optional.get());
} else {
// optional中没有值
System.out.println("Optional is empty");
}
复制代码
2.ifPresent()方法
在方法ifPresent()
中,咱们将传递一个Customer函数过去。仅当Optional对象中存在非空值时,才会执行该Consumer函数。
若是Optional对象中存放的是空值,则不执行任何操做:
optional.ifPresent(value -> {
System.out.println("Value present - " + value);
});
复制代码
在上面的代码中,咱们提供了lambda函数做为ifPresent()
方法的参数。
Optional的get()
方法仅用于从Optional对象取出值。若是Optional对象中保存的是一个空值,则将引起NoSuchElementException异常。
Employee employee = optional.get()
复制代码
在上述代码中,若是optional对象中是个保存的是空值,则会抛出异常,所以建议在使用get()
方法以前,咱们应该首先检查optional中是否有值。
若是Optional对象中保存的是空值,则返回调用orElse()
方法所传入的参数做为默认值。若是其中保存的是非空值,则返回其中保存的值。
请参见下面的示例:
// 使用三目表达式返回默认值
User finalEmployee = (employee != null) ? employee : new Employee("0", "Unknown Employee");
复制代码
如今,使用Optional的orElse()
方法实现与上面相同的逻辑:
// 使用orElse()方法返回默认值
User finalEmployee = optional.orElse(new Employee("0", "Unknown Employee"));
复制代码
咱们已经知道,若是Optional对象为空,则方法orElse()
直接返回调用时传入的参数做为默认值,而orElseGet()
方法接受一个 Supplier做为参数,而且当Optional对象中保存的是空值时调用Supplier。
Supplier返回的结果将成为Optional的默认值。
User finalEmployee = optional.orElseGet(() -> {
return new Employee("0", "Unknown Employee");
});
复制代码
若是Optional对象为空,则可使用orElseThrow()
方法抛出一个异常。
它能够用于REST API中指定的对象不存在的状况。您可使用此方法抛出自定义异常,例如ResourceNotFound()
等:
@GetMapping("/employees/{id}")
public User getEmployee(@PathVariable("id") String id) {
return employeeRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException("Employee not found with id " + id););
}
复制代码
假设您有一个Employee的Optional对象。如今,你正在检查员工的性别并相应地调用一个函数。
下面是这样作的旧方法:
if(employee != null && employee.getGender().equalsIgnoreCase("MALE")) {
// 调用函数
}
复制代码
如今,让咱们看看如何使用Optional的filter()
方法来达到此目的:
optional.filter(user -> employee.getGender().equalsIgnoreCase("MALE")) .ifPresent(() -> {
// 你的函数
})
复制代码
filter()
方法将一个谓词函数做为参数。若是Optional对象中包含一个非空值而且该值与提供的谓词匹配,则此方法将返回一个包含该值的Optional对象。
不然,此方法返回一个包含空值的Optional对象。
假设咱们有一个场景,咱们要提取雇员的地址,还要根据指定条件打印出该地址。
考虑如下示例:
咱们在Employee
类中有getAddress()
方法:
Address getAddress() {
return this.address;
}
复制代码
如下是实现上述需求的典型方式:
if (employee != null) {
Address address = employee.getAddress();
if (address != null && address.getCountry().equalsIgnoreCase("USA")) {
System.out.println("Employee belongs to USA");
}
}
复制代码
如今看看如何使用Optional的map()
方法实现同样的功能:
userOptional.map(Employee::getAddress).filter(address -> address.getCountry().equalsIgnoreCase("USA")).ifPresent(() -> {
System.out.println("Employee belongs to USA");
});
复制代码
与之前的方法相比,以上代码可读性强、简洁与高效。
让咱们更详细地分析一下代码:
// 使用map()方法提取地址
Optional<Address> addressOptional = employeeOptional.map(Employee::getAddress)
// 过滤出来自USA的员工
Optional<Address> usaAddressOptional = addressOptional.filter(address -> address.getCountry().equalsIgnoreCase("USA"));
// 若是员工来自USA则输出一些信息
usaAddressOptional.ifPresent(() -> {
System.out.println("Employee belongs to USA");
});
复制代码
在上述代码段中,出现如下任意一种状况,方法map()
返回一个包含空值的Optional对象:
employeeOptional
中不存在员工。getAddress()
返回null。不然,返回包含员工地址的Optional<Address>
对象。
如今,让咱们再次考虑上述与map()
方法相关的示例。
你能够看到即便Employee地址能够为null,咱们也没有使用Optional<Address>做为getAddress()
方法的返回类型。
若是getAddres()
方法的返回类型为Optional<Address>,则下面这一行代码将会出现问题:
Optional<Address> addressOptional = employeeOptional.map(Employee::getAddress)
复制代码
因为getAddress()
方法返回Optional <Address>,所以employeeOptional.map()
的返回类型是Optional<Optional<Address>>
。
下面是对于这种状况的演示:
Optional<Optional<Address>> addressOptional = employeeOptional.map(Employee::getAddress)
复制代码
这种状况下,咱们嵌套了Optional,可是咱们不但愿这样作,因此咱们如今可使用flatMap()
方法解决这个问题:
Optional<Address> addressOptional = employeeOptional.flatMap(Employee::getAddress)
复制代码
请注意,若是您的传递给
map()
方法的函数返回一个Optional对象,则应该使用flatMap()
方法替换掉map()
方法,从而获取到展开后的结果。
咱们已经学习了Java 8中的Optional是什么,它的优势以及在Java中经过使用Optional解决问题。此外,经过一些示例,咱们可以更好地明白Java 8中Optional类的一些方法。
谢谢阅读!
Java 8 Optional Uses and Best Practices