vlambda博客
学习文章列表

Java 8 Optional类--让代码更加优雅

今天总结整理一下关于Optional一些用法与原理性初探通过这个小神器可以让我们的代码变的更加优雅,同时也是一个面试点哦。

Optional采用链式编程的风格,可以帮助我们顺着一口气写完,避免了一层层的判断,下面就让我们开始体会一下Optional的优雅性和先进性吧。

问题描述

在大热的Spring Boot 2.0中,在将原来的泛型改为了Optional,旨在让我们的程式码更简洁。

Java 8 Optional类--让代码更加优雅

实践

Optional

很简单的一个类,点开它的原始码,其中所有的方法都是与null相关联的。

Java 8 Optional类--让代码更加优雅

这是一个简化我们处理null的类。

它就是一个容器,其中有我们想要的物件,但是该物件有时候会是空,所以我们需要使用Optional封装好的方法来获取需要的物件。从而很好地避免了空指标异常。

Java 8 Optional类--让代码更加优雅

错误示范

我看到网上很多人这么写:

catRepository . findById ( id ). get ();

下面是Spring Boot 1.5的写法,那请问:如果上面的写法是正确的,那为什么还要大费周章设计一个Optional呢?

catRepository . findOne ( id );

分析

通过get是能获取到我们需要的物件。

但是看看get的原始码,这样写,丢掷了NoSuchElementException异常,这个异常我们没法在全域性中处理它。

public T get () {  if ( value == null ) {  throw new NoSuchElementException ( "No value present" );  } return value ;  }

为什么不能再全域性中处理呢?大家可以思考一下:

因为NoSuchElementException覆盖的范围太广了,只要是Optional中有null就会丢掷NoSuchElementException,很多情况下都会造成这种异常,那我们究竟要给使用者一个什么样的提示资讯好呢?最后还是给出500伺服器异常,那异常处理的意义何在呢?

所以我们需要用Optional来丢掷一个有特定范围的能被全域性准确处理的异常。

Cat cat = catRepository.findOne ( id ); if ( null == cat ) {  throw new EntityNotFoundException ( "该实体找不到" ); } return cat ;

思想都是一样,我们不过是用一种更简洁的写法实现上面的功能。

实现

没错,就像下面一样,我们只需要一行程式码!

public Cat findById ( Long id ) {  return catRepository . findById ( id ). orElseThrow ( EntityNotFoundException :: new ); }

findById返回一个Optional,然后呼叫该物件的orElseThrow方法。

Java 8 Optional类--让代码更加优雅

orElseThrow方法,如果存在,返回包含的值,否则丢掷异常。

该方法的引数是一个lamda表示式。这里就不深究lamda表示式的几种型别了,如果感兴趣可以自行研究下FunctionConsumerPredicateSupplier这四个函式式介面的区别。

Java 8 Optional类--让代码更加优雅

所以传一个lamda表示式进去,然后IDEA会给出警告:

Java 8 Optional类--让代码更加优雅

这里的::lamda表示式的一种简写,是Java8中的新特性,看着可能有点奇怪,原来,编译器比程式设计师聪明多了。

异常处理

@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler ( EntityNotFoundException . class )  public ResponseEntity < String > entityNotFoundHandler () {  return new ResponseEntity <>( "您要找的实体不存在" , HttpStatus . NOT_FOUND ); }  }

写个控制器增强,全域性处理异常,这里的RestControllerAdvice又是一个组合注解:

Java 8 Optional类--让代码更加优雅

处理异常,同时以Json的格式返回。

@Test public void findById () throws Exception {  this . mockMvc . perform ( get ( "/cat/1" )) . andDo ( print ()); }

写个控制器的单元测试,查询一个不存在的实体,执行,看控制台的列印输出:

Java 8 Optional类--让代码更加优雅

一劳永逸

一劳永逸,这是我们最喜欢的东西了。

return catRepository . findById ( id ). orElseThrow ( EntityNotFoundException :: new );

以后再查询,就这一行,再也不用去判断null了。

NotNull

正所谓条条大路通罗马,对null的一劳永逸,我们这样实现,别人也可以那样实现。

如果你在Spring的专案中打过断点除错的话,那我断定你一定见过下面这行程式码:

Assert . notNull ();

以下是该方法的原始码,注意这里的Assertorg.springframework.util包下的:

Java 8 Optional类--让代码更加优雅

刚方法用于判断null,如果为空,则丢掷异常。

随便点开一个方法,都会在第一行为不该为null的引数进行判断。

这里,不禁对整个框架肃然起敬,同样一个方法,大牛写了二十分钟,而你写了十分钟,但是你却去改了半个小时的bug

@Nullable

可能在上面看到了我们不熟悉的注解@Nullable,表示从来没见过,这个注解干什么用的呢?

万能的StackOverflow又给出了完美的回答:

这会让你的程式码更清晰,如果你重写这个方法,你也需要让引数可为空。通常也用于程式码提示。

@Nullable@NotNull这一对注解,没什么实际意义,只是用于程式码更清晰,同时编译器能给出我们提示。