问题描述
在大热的 spring boot 2.0 中,在将原来的泛型改为了 optional ,旨在让我们的代码更简洁。
实践
optional
很简单的一个类,点开它的源代码,其中所有的方法都是与 null 相关联的。
这是一个简化我们处理 null 的类。
它就是一个容器,其中有我们想要的对象,但是该对象有时候会是空,所以我们需要使用 optional 封装好的方法来获取需要的对象。从而很好地避免了空指针异常。
错误示范
我看到网上很多人这么写:
1 |
catrepository.findbyid(id).get(); |
下面是 spring boot 1.5 的写法,那请问:如果上面的写法是正确的,那为什么还要大费周章设计一个 optional 呢?
1 |
catrepository.findone(id); |
分析
通过 get 是能获取到我们需要的对象。
但是看看 get 的源代码,这样写,抛出了 nosuchelementexception 异常,这个异常我们没法在全局中处理它。
1 2 3 4 5 6 |
public t get() { if (value == null ) { throw new nosuchelementexception( "no value present" ); } return value; } |
为什么不能再全局中处理呢?大家可以思考一下:
因为 nosuchelementexception 覆盖的范围太广了,只要是 optional 中有 null 就会抛出 nosuchelementexception ,很多情况下都会造成这种异常,那我们究竟要给用户一个什么样的提示信息好呢?最后还是给出 500 服务器异常,那异常处理的意义何在呢?
所以我们需要用 optional 来抛出一个有特定范围的能被全局准确处理的异常。
1 2 3 4 5 |
cat cat = catrepository.findone(id); if ( null == cat) { throw new entitynotfoundexception( "该实体找不到" ); } return cat; |
思想都是一样,我们不过是用一种更简洁的写法实现上面的功能。
实现
没错,就像下面一样,我们只需要一行代码!
1 2 3 |
public cat findbyid( long id) { return catrepository.findbyid(id).orelsethrow(entitynotfoundexception:: new ); } |
findbyid 返回一个 optional ,然后调用该对象的 orelsethrow 方法。
orelsethrow 方法,如果存在,返回包含的值,否则抛出异常。
该方法的参数是一个 lamda 表达式。这里就不深究 lamda 表达式的几种类型了,如果感兴趣可以自行研究下 function 、 consumer 、 predicate 、 supplier 这四个函数式接口的区别。
所以传一个 lamda 表达式进去,然后 idea 会给出警告:
can be replaced with method reference
该 lamda 表达式能被一个方法引用代替, alt + enter ,我们最终的代码就长这样:
这里的 :: 是 lamda 表达式的一种简写,是 java8 中的新特性,看着可能有点奇怪,原来,编译器比程序员聪明多了。
异常处理
1 2 3 4 5 6 7 8 |
@restcontrolleradvice public class globalexceptionhandler {
@exceptionhandler (entitynotfoundexception. class ) public responseentity<string> entitynotfoundhandler() { return new responseentity<>( "您要找的实体不存在" , httpstatus.not_found); } } |
写个控制器增强,全局处理异常,这里的 restcontrolleradvice 又是一个组合注解:
处理异常,同时以 json 的格式返回。
1 2 3 4 5 |
@test public void findbyid() throws exception { this .mockmvc.perform(get( "/cat/1" )) .anddo(print()); } |
写个控制器的单元测试,查询一个不存在的实体,运行,看控制台的打印输出:
一劳永逸
一劳永逸,这是我们最喜欢的东西了。
1 |
return catrepository.findbyid(id).orelsethrow(entitynotfoundexception:: new ); |
以后再查询,就这一行,再也不用去判断 null 了。
notnull
正所谓条条大路通罗马,对 null 的一劳永逸,我们这样实现,别人也可以那样实现。
如果你在 spring 的项目中打过断点调试的话,那我断定你一定见过下面这行代码:
1 |
assert .notnull(); |
以下是该方法的源码,注意这里的 assert 是 org.springframework.util 包下的:
刚方法用于判断 null ,如果为空,则抛出异常。
随便点开一个方法,都会在第一行为不该为 null 的参数进行判断。
这里,不禁对整个框架肃然起敬,同样一个方法,大牛写了二十分钟,而你写了十分钟,但是你却去改了半个小时的 bug 。
@nullable
可能在上面看到了我们不熟悉的注解 @nullable ,表示从来没见过,这个注解干什么用的呢?
万能的 stackoverflow 又给出了完美的回答:
这会让你的代码更清晰,如果你重写这个方法,你也需要让参数可为空。通常也用于代码提示。
@nullable 和 @notnull 这一对注解,没什么实际意义,只是用于代码更清晰,同时编译器能给出我们提示。
总结
之前一直抱怨 java 更新的太快,学校教的是 java5 之前的东西,从 java5 开始有的注解,但是从来没讲过这个东西,然而去看看官方的描述:
其实, java 的每次更新,都是为了我们更简洁优雅的代码而努力。去看看官方的描述, java 让我们将更多的精力放在 think 上,而不是 code 上。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
原文链接:https://segmentfault测试数据/a/1190000016502649
查看更多关于Java Optional实践(小结)的详细内容...