请遵循以下十二种最佳实践,以保护您的应用程序免受丑陋的空指针异常的侵扰,并使您的代码更具可读性和简洁性。
每个认真的Java开发人员或架构师都曾经听说过或经历过NullPointerException异常的滋扰。Java工程师一直致力于解决该null问题很长时间,在Java 8中,添加了一个称为的新类型Optional<T>,Optional原本是一种返回类型,当与流(或return的方法Optional)组合以构建流畅的API时使用。此外,它旨在帮助开发人员null正确处理引用。
下面谈谈如何避免使用Optional反模式? 或者说不正确用法:
1:切勿将null分配给可选变量。
有时,当开发人员正在处理数据库以寻找Employee时,他们设计了return的方法Optional<Employee>。我发现null如果数据库没有返回任何结果,开发人员仍会返回,例如:
public Optional<Employee> getEmployee(int id) { |
上面的代码不正确,应该完全避免。要更正它,您应该将第3行替换为以下行,该行Optional以空初始化Optional:
Optional<Employee> employee = Optional.empty(); |
2:不要直接调用get()
Optional<Employee> employee = HRService.getEmployee(); |
您是否猜到“Employee”Optional里面是空着,所以get()直接打电话会抛出一个“ java.util.NoSuchElementException?”。您应该始终首先使用该isPresent()方法检查值的存在,如下所示:
if (employee.isPresent()) { |
请注意,上面的代码是样板代码,现实中这样做是不可取的。接下来,您将看到很多优雅的呼叫isPresent()/get()对替代方法。
3. 避免使用isPresent()和get()对来设置和返回值。
public static final String DEFAULT_STATUS = "Unknown"; |
只需使用orElse()替换isPresent()和get()配对,如以下:
public String getEmployeeStatus(long id) { |
这里要考虑的一个非常重要的注意事项是可能的性能损失:orElse()无论可选值的存在与否,总是会评估返回的值。因此,这里的规则是orElse()在您已经预先构造了值并且不使用昂贵的计算值时使用。
4. 请勿使用orElse()返回计算值。
避免使用orElse()返回计算值,因为这会降低性能。考虑以下代码片段:
Optional<Employee> getFromCache(int id) { |
首先,代码尝试从缓存中获取具有给定ID的Employee,如果该Employee不在缓存中,则尝试从数据库中获取Employee。然后,如果员工不在缓存或数据库中,则代码将引发一个NotFoundException。如果运行此代码,而雇员在缓存中,则会打印以下内容:
Search in cache with Id: 1 |
即使将Employee从缓存中返回,数据库查询仍然被调用。很昂贵,对不对?取而代之的是,我将使用orElseGet(Supplier<? extends T> supplier),就像,orElse()但有一个区别:如果theOptional为空,则orElse()直接返回默认值,而orElseGet()允许您传递Supplier仅当theOptional为空时才调用的函数。这对性能非常有用。
public Employee findEmployee(int id) { |
这次,您将获得所需的内容并提高了性能:该代码将仅打印以下内容:
Search in cache with Id: 1 |
不要考虑使用isPresent()和get()配对,因为它们并不雅致。
orElseThrow()方法自Java 10起就存在。
5.如果仅当存在Optional值时才想执行操作,请不要使用isPresent()-get()。
Optional<String> confName = Optional.of("CodeOne"); |
只需将上面的第2行和第3行替换为一行,如下所示:
confName.ifPresent( s -> System.out.println(s.length())); |
ifPresent()方法不返回任何内容,并且自Java 8起就存在。
如果不存在值,则不要使用isPresent()-get()执行基于空的操作。6.
开发人员有时会编写代码,如果存在Optional值,该代码会执行某些操作,但如果不存在,则执行基于空的操作,如下所示:
Optional<Employee> employee = ... ; |
请注意,ifPresentOrElse()就像是ifPresent(),唯一的区别是,它涵盖了else分支为好。因此,您可以将第2行到第6行替换为:
employee.ifPresentOrElse( |
ifPresentOrElse()方法自Java 9起就存在。
7.不存在任何值时,返回另一个Optional。
在某些情况下,如果Optional中有值存在,则返回一个Optional描述值的描述;否则,返回Optional由供应功能产生的产品。避免执行以下操作:
Optional<String> defaultJobStatus = Optional.of("Not started yet."); |
不要过度使用orElse()ororElseGet()方法来完成此操作,因为这两个方法都返回一个未包装的值。因此,也请避免执行以下操作:
public Optional<String> fetchJobStatus(int jobId) { |
完美而优雅的解决方案是使用该 (Supplier<? extends Optional<? extends T>> supplier)方法,如下所示:
public Optional<String> fetchJobStatus(int jobId) { |
或者,即使没有defaultJobStatus在开始时定义可选的内容,也可以使用以下代码替换第3行中的代码:
return foundStatus.or(() -> Optional.of("Not started yet.")); |
如果提供函数为或产生结果,则or()抛出NullPointerExceptionnull异常。从Java 9开始就存在此方法。
8.无论是否为空,都获取Optional的状态。
从Java 11开始,您可以Optional使用isEmpty()方法直接检查an是否为空。
public boolean isMovieListEmpty(int id){ |
您可以将第3行替换为以下行,以使代码更具可读性:
return movieList.isEmpty(); |
9.不要过度使用Optional。
有时,开发人员倾向于过度使用他们喜欢的东西,而Optional类就是其中之一。
1 public String fetchJobStatus(int jobId) { |
通过使用以下更清晰的代码行替换第3行,可以很简单:
return status == null ? "Not started yet." : status; |