在文章的开头,先说下npe问题,npe问题就是,我们在开发中经常碰到的nullpointerexception.假设我们有两个类,他们的uml类图如下图所示
在这种情况下,有如下代码
user.getaddress().getprovince();
这种写法,在user为null时,是有可能报nullpointerexception异常的。为了解决这个问题,于是采用下面的写法
if(user!=null){
address address = user.getaddress();
if(address!=null){
string province = address.getprovince();
}
}
这种写法是比较丑陋的,为了避免上述丑陋的写法,让丑陋的设计变得优雅。java8提供了optional类来优化这种写法,接下来的正文部分进行详细说明
api介绍
先介绍一下api,与其他文章不同的是,本文采取类比的方式来讲,同时结合源码。而不像其他文章一样,一个个api罗列出来,让人找不到重点。
1、optional(t value),empty(),of(t value),ofnullable(t value)
这四个函数之间具有相关性,因此放在一组进行记忆。
先说明一下,optional(t value),即构造函数,它是private权限的,不能由外部调用的。其余三个函数是public权限,供我们所调用。那么,optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空。好吧,这么说还是比较抽象。直接上optional(t value)构造函数的源码,如下图所示
那么,**of(t value)**的源码如下
public static 《t》 optional《t》 of(t value) {
return new optional《》(value);
}
也就是说of(t value)函数内部调用了构造函数。根据构造函数的源码我们可以得出两个结论:
通过of(t value)函数所构造出的optional对象,当value值为空时,依然会报nullpointerexception。
通过of(t value)函数所构造出的optional对象,当value值不为空时,能正常构造optional对象。
除此之外呢,optional类内部还维护一个value为null的对象,大概就是长下面这样的
public final class optional《t》 {
//省略。..。
private static final optional《?》 empty = new optional《》();
private optional() {
this.value = null;
}
//省略。..
public static《t》 optional《t》 empty() {
@suppresswarnings(“unchecked”)
optional《t》 t = (optional《t》) empty;
return t;
}
}
那么,empty()的作用就是返回empty对象。
好了铺垫了这么多,可以说ofnullable(t value)的作用了,上源码
public static 《t》 optional《t》 ofnullable(t value) {
return value == null ? empty() : of(value);
}
好吧,大家应该都看得懂什么意思了。相比较of(t value)的区别就是,当value值为null时,of(t value)会报nullpointerexception异常;ofnullable(t value)不会throw exception,ofnullable(t value)直接返回一个empty对象。
那是不是意味着,我们在项目中只用ofnullable函数而不用of函数呢?
不是的,一个东西存在那么自然有存在的价值。当我们在运行过程中,不想隐藏nullpointerexception。而是要立即报告,这种情况下就用of函数。但是不得不承认,这样的场景真的很少。博主也仅在写junit测试用例中用到过此函数。另外关注:架构师专栏,在后台回复:“面试题”可以获取,高清pdf最新版3625页互联网大厂面试题。
2、orelse(t other),orelseget(supplier《? extends t》 other)和orelsethrow(supplier《? extends x》 exceptionsupplier)
这三个函数放一组进行记忆,都是在构造函数传入的value值为null时,进行调用的。orelse和orelseget的用法如下所示,相当于value值为null时,给予一个默认值:
@test
public void test() {
user user = null;
user = optional.ofnullable(user).orelse(createuser());
user = optional.ofnullable(user).orelseget(() -》 createuser());
}
public user createuser(){
user user = new user();
user.setname(“zhangsan”);
return user;
}
这两个函数的区别:当user值不为null时,orelse函数依然会执行createuser()方法,而orelseget函数并不会执行createuser()方法,大家可自行测试。
至于orelsethrow,就是value值为null时,直接抛一个异常出去,用法如下所示
user user = null;
optional.ofnullable(user).orelsethrow(()-》new exception(“用户不存在”));
3、map(function《? super t, ? extends u》 mapper)和flatmap(function《? super t, optional《u》》 mapper)
这两个函数放在一组记忆,这两个函数做的是转换值的操作。
直接上源码
public final class optional《t》 {
//省略。..。
public《u》 optional《u》 map(function《? super t, ? extends u》 mapper) {
objects.requirenonnull(mapper);
if (!ispresent())
return empty();
else {
return optional.ofnullable(mapper.apply(value));
}
}
//省略。..
public《u》 optional《u》 flatmap(function《? super t, optional《u》》 mapper) {
objects.requirenonnull(mapper);
if (!ispresent())
return empty();
else {
return objects.requirenonnull(mapper.apply(value));
}
}
}
这两个函数,在函数体上没什么区别。唯一区别的就是入参,map函数所接受的入参类型为function《? super t, ? extends u》,而flapmap的入参类型为function《? super t, optional《u》》。
在具体用法上,对于map而言:
如果user结构是下面这样的
public class user {
private string name;
public string getname() {
return name;
}
}
这时候取name的写法如下所示
string city = optional.ofnullable(user).map(u-》 u.getname()).get();
对于flatmap而言:
如果user结构是下面这样的
public class user {
private string name;
public optional《string》 getname() {
return optional.ofnullable(name);
}
}
这时候取name的写法如下所示
string city = optional.ofnullable(user).flatmap(u-》 u.getname()).get();
4、ispresent()和ifpresent(consumer《? super t》 consumer)
这两个函数放在一起记忆,ispresent即判断value值是否为空,而ifpresent就是在value值不为空时,做一些操作。这两个函数的源码如下
public final class optional《t》 {
//省略。..。
public boolean ispresent() {
return value != null;
}
//省略。..
public void ifpresent(consumer《? super t》 consumer) {
if (value != null)
consumer.accept(value);
}
}
需要额外说明的是,大家千万不要把
if (user != null){
// todo: do something
}
给写成
user user = optional.ofnullable(user);
if (optional.ispresent()){
// todo: do something
}
因为这样写,代码结构依然丑陋。博主会在后面给出正确写法
至于ifpresent(consumer《? super t》 consumer),用法也很简单,如下所示
optional.ofnullable(user).ifpresent(u-》{
// todo: do something
});
5、filter(predicate《? super t》 predicate)
不多说,直接上源码
public final class optional《t》 {
//省略。..。
objects.requirenonnull(predicate);
if (!ispresent())
return this;
else
return predicate.test(value) ? this : empty();
}
filter 方法接受一个 predicate 来对 optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 optional;否则返回 optional.empty。
用法如下
optional《user》 user1 = optional.ofnullable(user).filter(u -》 u.getname().length()《6);
如上所示,如果user的name的长度是小于6的,则返回。如果是大于6的,则返回一个empty对象。
实战使用
例一
在函数方法中
以前写法
public string getcity(user user) throws exception{
if(user!=null){
if(user.getaddress()!=null){
address address = user.getaddress();
if(address.getcity()!=null){
return address.getcity();
}
}
}
throw new excpetion(“取值错误”);
}
java8写法
public string getcity(user user) throws exception{
return optional.ofnullable(user)
.map(u-》 u.getaddress())
.map(a-》a.getcity())
.orelsethrow(()-》new exception(“取指错误”));
}
例二
比如,在主程序中
以前写法
if(user!=null){
dosomething(user);
}
java8写法
optional.ofnullable(user)
.ifpresent(u-》{
dosomething(u);
});
例三
以前写法
public user getuser(user user) throws exception{
if(user!=null){
string name = user.getname();
if(“zhangsan”.equals(name)){
return user;
}
}else{
user = new user();
user.setname(“zhangsan”);
return user;
}
}
java8写法
public user getuser(user user) {
return optional.ofnullable(user)
.filter(u-》“zhangsan”.equals(u.getname()))
.orelseget(()-》 {
user user1 = new user();
user1.setname(“zhangsan”);
return user1;
});
}
其他的例子,不一一列举了。不过采用这种链式编程,虽然代码优雅了。但是,逻辑性没那么明显,可读性有所降低,大家项目中看情况酌情使用。
Symbio正与日产丰田合作提高机器人效率
中国的光刻机与荷兰光刻机技术相比,主要区别是什么
笔记本售后服务,用户不能承受之痛
讯景RX5700系列非公版显卡发布 售价3299元
基于RFID技术和物联网技术的车辆防盗怎样实现
如何用一行代码解决空指针问题
国产生物芯片如何追赶国际水平?
RTOS信号量、队列通信原理
如果比特币可以支付买食物的话也就意味着我们可以靠比特币生活
小米6将华丽转身;金属机身,曲面屏+超声波指纹识别,但可能有两版本!
293D系列固钽贴片电容器的额定电压提高至63V
CATL计划将在2019年基于NCM811材料推出新一代高镍低钴的电池产品
人工智能的全面发展到底是不是意味着人类的结束
台积电、日月光同时抢食异构芯片整合商机
谷歌公布FuchsiaOS部分细节 采用全新内核将实现桌面跨平台
协作机器人提升包裹分拣速度 在快递行业发挥着越来越重要的作用
Medtec医疗设计制造展八大看点,有源医疗器械核心部件领风骚
Apollo导航模式详细教程
国产MCU发展的四大阶段总结
热控镍镉充电器电路图