1、使用object==null的例子

2、null带来的问题

3、其他语言中null的处理(替代)

4、Java8的Optional类

  4.1 这样做有什么好处呢?

  4.2 引入Optional类的目的

  4.3 null与Optional.empty()

  4.4 使用Optional

  4.5 使用Optional域,该域无法序列化

  4.6 应用

参考文献

1、使用object==null的例子

例1

pulbic String getCarInsuranceName(Person person){
if(person != null){
Car car = person.getCar();
if(car != null){
Insurance insurance = car.getInsurance();
if(insurance != null){
return insurance.getName();
}
}
}
return "UNKNOWN";
}

可以发现这样写比较繁琐,每当某个变量可能为null时,都要添加if块来判断,增加了代码的缩进级别,扩展性和可读性都很差,且万一忘记判断某个变量,依然出现NPE。

例2

public String getCarInsuranceName(Person person){
if(person == null){
return "unknown";
}
Car car = person.getCar();
if(car == null){
return "unknown";
}
Insurance insurance = car.getInsurance();
if(insurance == null){
return "unknown";
}
return insurance.getName();
}

这种方式避免深层嵌套的if块,但是每一个null检查点都增加一个退出点,难以维护,且为null时,返回的字符串“unknown”重复出现。同样的,当忘记检查某个可能为null的属性时,会出现NPE。

2、null带来的问题

NPE是目前Java程序开发种最典型的异常;

会使代码膨胀,深度嵌套的null检查,代码的可读性差;

null自身是毫无意义的,null没有任何语义;

破坏了Java的哲学,Java一直试图避免让程序员意识到指针,唯一的例外就是null指针;

null在Java的类型系统上成了例外,null不属于任何类型,即它可以赋值给任意引用类型,当这个变量被传递到系统的另一个部分后,将无法获知这个null变量最初的赋值到底什么类型。

3、其他语言中null的处理(替代)

Groovy:安全导航操作符(safe navigation operator, 标记为?)

e.g. def carInsuranceName = person?.car?.insurance?.name 当调用链中的变量遇到null时将null引用沿着调用链传递下去,返回一个null。

函数式语言:

Haskell:Maybe类型,其本质上是对optional值的封装。Maybe类型的变量可以是指定类型的值,也可以什么都不是。没有null引用的概念。

Scala:Option[T],既可以包含类型为T的变量,也可以不包含该变量,显式调用Option类型的available操作,检查该变量是否有值,即变相的“null”检查。

4、Java8的Optional类

Java8引入Optional类,在从null到Optional的迁移中,需要反思的是:如何在你的域模型中使用Optional值。

我们根据上面例子的类关系可知,Person的Car变量存在null情况,因此不能直接声明为Car,改为Optional<Car>。

4.1 这样做有什么好处呢?

使用Optional<Car>声明变量,清楚的表明了这里的变量缺失是允许的,而使用Car类型,可能将变量赋值为null,就只能依赖业务模型的理解,判断一个null是否属于该变量的有效范畴。

我们根据Optioanl类重新定义Person/Car/Insurance的数据模型:

public class Person{
private Optional<Car> car;
public Optional<Car> getCar() {return car;}
}
public class Car{
private Optional<Insurance> insurance;
public Optional<Insurance> getInsurance(){return insurance;}
}
public class Insurance{
private String name; // 保险公司必须有名字
public String getName(){return name;}
}

当获取insurance公司名称发生NPE,就能非常确定出错的原因,不需要为其添加null的检查,因为null的检查只会掩盖问题,并未真正地修复问题。insurance公司必须有名字,当公司没有名称,就需要查看出了什么问题,而不应该再添加一段代码,将这个问题隐藏。

4.2 引入Optional类的目的

代码中始终如一使用Optional,能非常清晰地节点出变量值的缺失是结构上的问题还是算法上或是数据中的问题。

引入Optional并不是要消除每一个null引用,而是帮助设计更普适的API,看见签名就能了解是否接受一个Optional的值,这种强制会让你更积极地将变量从Optional中解包出来,直面缺失的变量值。

即看见Optional<Car>就可以知道该变量car可能为null。

4.3 null与Optional.empty()

尝试解引用一个null,一定会触发NPE,而使用Optional.empty()就没事,其为Optioanl类的一个有效对象,多种场景都能调用,非常有用。

Optional.empty()的定义如下

/**
* Returns an empty {@code Optional} instance. No value is present for this
* Optional.
*
* @apiNote Though it may be tempting to do so, avoid testing if an object
* is empty by comparing with {@code ==} against instances returned by
* {@code Option.empty()}. There is no guarantee that it is a singleton.
* Instead, use {@link #isPresent()}.
*
* @param <T> Type of the non-existent value
* @return an empty {@code Optional}
*/
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}

EMPTY的定义如下:

/**
* Common instance for {@code empty()}.
*/
private static final Optional<?> EMPTY = new Optional<>();

4.4 使用Optional

(1) 使用map从Optional对象中提取和转换值

// 原先写法
String name = null;
if (insurance != null){
name = insurance.getName();
} // 使用Optional
Optional<Insurance> optInsurance = Optional.ofNullable(insurance);
Optional<String> name = optInsurance.map(Insurance::getName);

stream和map方法:

(2)使用flatMap方法连接Optional对象

// 原先写法
public String getCarInsuranceName(Person person){
return person.getCar().getInsurance().getName();
} // 使用Optional.map会报错
/*因为Person的类型是Optional<Computer>,调用map方法是正常的,
但是,getCar()方法返回的是一个Optional<Car>对象,
即这个map操作后得到的类型是Optional<Optional<Car>>.
对一个Optional对象调用getInsurance ()是非法的。*/
public String getCarInsuranceName(Optional<Person> person){
return person.map(Person::getCar)
.map(Car::getInsurance)
.map(Insurance::getName)
.orElse("UNKNOWN");
} // 使用Optional.flatMap
public String getCarInsuranceName(Optional<Person> person){
return person.flatMap (Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("UNKNOWN");
}

stream和flatMap方法比较:

flatMap方法连接Optional对象过程:

(3)map和flatMap方法源码

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)); // Optional.ofNullable()会返回一个Optional<>
}
} //如果调用的mapper返回已经是Optional,则调用该mapper后,flatMap不会再添加Optional
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)); //返回参数的类型
}
}

Objects类的requireNonNull方法:

public static <T> T requireNonNull(T obj) {
if (obj == null)
throw new NullPointerException();
return obj;
}

4.5 使用Optional域,该域无法序列化

在域模型中使用Optional,该域无法序列化,因为Optional没有实现Serializable接口。

Java语言的架构师Brain Goetz明确陈述过:Optional的设计初衷仅仅是要支持能返回Optional对象的语法。

Optional类设计时就没有特别考虑将其作为类的字段使用,所以它并未实现Serializable接口。

若一定要序列化域,替代方案:

public class Person{
private Car car;
public Optional<Car> getCarOptional(){
return Optional.ofNullable(car);
}
}

4.6 应用

(1)用Optional封装可能为null的值

Object value = map.get("key");
↓↓
Optional<Object> value = Optional.ofNullable(map.get("key"));

(2)异常

Integer.parseInt(String) --> NumberFormatException
↓↓
public static Optional<Integer> stringToInt(String s){
try{
return Optional.of(Integer.parseInt(s));
} catch(NumberFormatException e){
return Optional.empty();
}
}

(3)基础类型的Optonal对象

Optional也提供了基础类型--OptionalInt, OptionalLong, OptionalDouble,但是不推荐使用,因为基础类型的Optional不支持map、flatMap以及filter方法。

(4)整合

Properties props = new Properties();
props.setProperty("a", "5");
props.setProperty("b", "true");
props.setProperty("c", "-3");

需求:从这些属性中读取一个值,该值的含义是时间,单位s,所以必须≥0.

public int readDuration(Properties props, String name){
String value = props.getProperty(name);
if (value != null){
try{
int i = Integer.parseInt(value);
if (i > 0){
return i;
}
}catch(NumberFormatException nfe){}
}
return 0;
} ↓↓ public int readDuration(Properties props, String name){
return Optional.ifNullable(props.getProperty(name))
.flatMap(OptionalUtil::stringToInt) // ②中的方法
.filter(i -> i > 0)
.orElse(0);
}

参考文献:《Java8实战》(英)Urma, R.G. (意)Fusco, M. (英)Mycroft, A,著;陆明刚 劳佳译. 北京:人民邮电出版社,2016.4

官网:https://www.oracle.com/technical-resources/articles/java/java8-optional.html

Optional类与使用==判断null有什么区别?使用Optional类有什么优势?的更多相关文章

  1. java中List接口的实现类 ArrayList,LinkedList,Vector 的区别 list实现类源码分析

    java面试中经常被问到list常用的类以及内部实现机制,平时开发也经常用到list集合类,因此做一个源码级别的分析和比较之间的差异. 首先看一下List接口的的继承关系: list接口继承Colle ...

  2. CSS3伪类和伪元素的特性和区别尤其是 ::after和::before

    伪类和伪元素的理解 官方解释: 伪类一开始单单只是用来表示一些元素的动态状态,典型的就是链接的各个状态(LVHA).随后CSS2标准扩展了其概念范围,使其成为了所有逻辑上存在但在文档树中却无须标识的“ ...

  3. JAVA8新特性Optional,非空判断

    Optional java 的 NPE(Null Pointer Exception)所谓的空指针异常搞的头昏脑涨, 有大佬说过 "防止 NPE,是程序员的基本修养." 但是修养归 ...

  4. Atitit. null错误的设计 使用Optional来处理null

    Atitit. null错误的设计 使用Optional来处理null 然后,我们再看看null还会引入什么问题. 看看下面这个代码: String address = person.getCount ...

  5. 关于 JavaScript 的 null 和 undefined,判断 null 的真实类型

    null.undefined 博客地址: https://ainyi.com/39 undefined:表示一个变量最原始的状态,而非人为操作的结果 null:表示一个对象被人为的重置为空对象,而非一 ...

  6. java8 Optional优雅非空判断

    java8 Optional优雅非空判断 import java.util.ArrayList;import java.util.List;import java.util.Optional; pub ...

  7. php中函数 isset(), empty(), is_null() 的区别,boolean类型和string类型的false判断

    php中函数 isset(), empty(), is_null() 的区别,boolean类型和string类型的false判断 实际需求:把sphinx返回的结果放到ssdb缓存里,要考虑到sph ...

  8. JS中判断null、undefined与NaN的方法

    写了个 str ="s"++; 然后出现Nan,找了一会. 收集资料如下判断: 1.判断undefined: 代码如下: <span style="font-siz ...

  9. JS中 判断null

    以下是不正确的方法: var exp = null; if (exp == null) { alert("is null"); } exp 为 undefined 时,也会得到与 ...

  10. JS中如何判断null、undefined与NaN

    1.判断undefined: <span style="font-size: small;">var tmp = undefined; if (typeof(tmp)  ...

随机推荐

  1. npm i error:0909006C:PEM routines:get_name:no start line 遇到问题解决

    找了大半天的问题,结果是有个httpd的线程开机自动启动,把端口占用了

  2. AWT+Swing区别

    AWT 是Abstract Window ToolKit (抽象窗口工具包)的缩写,这个工具包提供了一套与本地图形界面进行交互的接口.AWT 中的图形函数与操作系统所提供的图形函数之间有着一一对应的关 ...

  3. thinkphp6+composer+无集成工具 配置php项目环境

    安装composer 下载地址:https://getcomposer.org/Composer-Setup.exe 安装步骤 点击finish完成即可. 打开cmd输入composer查看是否安装成 ...

  4. django解决网站CORS前后端跨域问题

    1.安装cors-headers⼯具   pip install django-cors-headers 2.安装cors-headers应⽤ # 注册应用 INSTALLED_APPS = [ 'd ...

  5. uniapp 配置钉钉小程序package.json文件

    { "uni-app": { "scripts": { "mp-dingtalk": { "title": " ...

  6. 看K线学炒股(8.10)

    今天大盘看起来疲软但强势的一天,收涨1.01%. 广东骏亚,现价21.39,看尾盘,这只票现在看还有下跌空间,但也有反弹可能,跌到21元以下均价上可加仓,博止跌反弹.现在60分钟线看有点阴雨绵绵的意思 ...

  7. java 守护线程的关闭

    在进程内所有用户线程 全部消亡后,如果 守护线程仍在执行 ( 注意: 守护线程并不是一直运行中,守护线程中的代码执行完毕,则守护线程自然消亡. ),则会被强制消亡.

  8. java的集合以及数据结构

    一.集合 1.介绍 红色为接口 蓝色为实现类 2.三种遍历方式 迭代器 增强for lambda表达式 Integer[] arr = col.toArray(new Integer[col.size ...

  9. 部分jdk网盘链接

    链接:https://pan.baidu.com/s/1Nw84qVRL3Buarh2LY1lWEg 提取码:6q2z 含 6u45 7u80 8u202 11.0.X 的win及linux版 没有网 ...

  10. Python学习笔记文件读写之生成随机的测试试卷文件

    随笔记录方便自己和同路人查阅. #------------------------------------------------我是可耻的分割线--------------------------- ...