对null进行处理

程序中经常需要对null情况进行处理,比如Course类中有一个List stuList属性,Student有一个name属性。

现在想要查看某个student的name属性的长度,不负责任(不处理null)的写法如下:

Course course = new Course("数学");
Student s0 = new Student("s0");
Student s1 = new Student(null);
Student s2 = null;
course.addStudent(s0, s1, s2);
int index = 1;
String name = course.getStuList().get(index).getName(); //index = 1或2都会抛出NullPointerException
System.out.println(name.length());

String name = course.getStuList().get(index).getName()这样写固然省事,但是当s2为null或者s1对象的name为null时,都会抛出大家熟悉的NullPointerException

所以需要使用大量的if...else语句对变量否为null进行判断。

int index = 1;
Student student = course.getStuList().get(index);
String errorMessage = "处理过程中无null";
String name = "";
if(student!=null) {
name = student.getName();
if(name!=null) {
System.out.println(name.length());
}else {
errorMessage="student对象的name为null"; }
}else {
errorMessage="student对象为null";
}
sysout
System.out.println(errorMessage);

使用Optional处理null

null是一个特殊值,并不是一个类型,student.getName()可能返回null也可能返回String类型对象,这是两种不同的情况。

Java 8中引入了Optional类型来统一处理null与对应的类型。该类型为容器类型,可以包含某个类型的非空值与空值,所以可以对他们统一处理。

Optional基本用法如下:

Optional<Student> stuNullable = Optional.empty();//代表null的Optional对象
Student stu = new Student("s0");
System.out.println(stuNullable.isPresent());//false,判断里面是否存在Student对象
//Student student = stuNullable.get();//将抛出NoSuchElementException
Optional<Student> stuNotNull = Optional.ofNullable(stu); //代表非空的Student,实际上将stu对象放入该Optional容器中
System.out.println(stuNotNull.isPresent());//true
Student student = stuNotNull.get(); //返回刚才装入的stu对象

其中 Optional.ofNullable(stu)的作用是:当stu为null时,返回Optional.empty(),否则返回包含stu对象的Optional对象(即,Optional.of(stu))。

到现在为止我们可以使用Optional来代替前面的对null值的直接处理,代码如下:

Course course = new Course("数学");
Student s0 = new Student("s0");
Student s1 = new Student(null);
Student s2 = null;
course.addStudent(s0, s1, s2);
int index = 1;
Student student = course.getStuList().get(index);
Optional<Student> stu = Optional.ofNullable(student);//将student放入Optioanl中进行处理
String errorMessage = "处理过程中无null";
String name = "";
if (stu.isPresent()) {
Optional<String> nameOfNullable = Optional.ofNullable(stu.get().getName());
if (nameOfNullable.isPresent()) {
name = nameOfNullable.get();
System.out.println(name.length());
}else {
errorMessage="student对象的name为null";
}
}else {
errorMessage="student对象为null";
}
System.out.println(errorMessage);

呃.....代码更复杂了。但至少让你不能忽略null值了。

再看看Optional中的其他方法ifPresentorElse

stu.ifPresent(e->System.out.println(e.getName()));//如果存在对象,则Optional中的包含的对象getName返回的值
Student defaultStudent = new Student("默认对象");
Student orElse = stu.orElse(defaultStudent); //如果存在对象直接返回该对象,否则返回defaultStudent对象

似乎无补于事。

Optional的map方法

对上面的代码可以使用map方法进行改造,如下所示:

int index = 0;
String errorMessage = "处理过程有null";
String name = "";
Optional<Course> courseNullable = Optional.ofNullable(course);
Optional<String> resultOptional = courseNullable.map(e->e.getStu(index)).map(e->e.getName());
String result = resultOptional.orElse(errorMessage);//resultOptional中可能为null,也可能有值。如果未null,则返回errorMessage。
System.out.println(result);

map方法入参为Function类型(可以将Optional中原来的类型转换成新的类型)。

map(e->e.getStu(index))就是将Courset类型转换成Student类型

map(e->e.getName())则是将Student类型转换成String类型。

map方法返回值为Optional类型,所以可以以链式风格map(e->e.getStu(index)).map(e->e.getName())取代上面复杂的if...else处理。

这个例子体现出了Optional的优越性,即可以通过其优雅的处理null值。

窃以为,相比较Optioanl带来的优越性,其语法还是有点复杂。建议先掌握其语法,这样至少看别人的相关代码的时候不会无所适从。

基础代码


class Student {
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public Student(String name) {
this.name = name;
} @Override
public String toString() {
return "Student [name=" + name + "]";
} } class Course {// 课程
private String name;
private List<Student> stuList; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public void addStudent(Student... stus) {
for (Student student : stus) {
stuList.add(student);
}
} public Student getStu(int i) {
return stuList.get(i);
} public List<Student> getStuList() {
return stuList;
} public Course(String name) {
this.name = name;
stuList = new ArrayList<>();
} }

使用Optional改造Course

查看jdk文档,Optional的说明如下:

Optional is primarily intended for use as a method return type where there is a clear need to represent "no result," and where using null is likely to cause errors.

即,Optional主要用于修饰方法的返回类型,来表示“没有结果”这个返回结果,还用在使用null作为返回类型容易导致出错的地方。

那么,我么可以使用Optional来改造Course的Student getStu(int i)代码:

	public Student getStu(int i) {
return stuList.get(i);
}

该段代码主要有两个问题:

  1. i可能越界。
  2. get(i)返回的值可能为null。

改造后代码如下:

public Optional<Student> getStu(int i) {
if (i >= stuList.size())
return Optional.empty(); // 带代表null的Optional实例
Student student = stuList.get(i);
if (student == null)
return Optional.empty();
return Optional.of(student);
}

或者进一步简化,改造成这样:

public Optional<Student> getStu(int i) {
if (i >= stuList.size())
return Optional.empty();
Student student = stuList.get(i);
return Optional.ofNullable(student);
}

Optional.ofNullable方法可以处理入参为null的情况。其相关文档描述如下

Returns an Optional describing the specified value, if non-null, otherwise returns an empty Optional.

定义测试方法如下

private static void getStuByIndex(Course course, int index) {
Student stu0 = course.getStu(index).orElse(new Student("默认学生"));//如果为null,则返回“默认学生”对象
System.out.println(stu0);
}

测试代码如下:

Course course = new Course("数学");
Student s0 = new Student("s0");
Student s1 = new Student(null);
Student s2 = null;
course.addStudent(s0, s1, s2);
getStuByIndex(course, 0);
getStuByIndex(course, 1);
getStuByIndex(course, 2);//s2为null
getStuByIndex(course, 3);//越界,返回Optional.empty()

输出:

Student [name=s0]
Student [name=null]
Student [name=默认学生]
Student [name=默认学生]

测试flatmap代码如下

Optional<Course> courseNullable = Optional.ofNullable(course);
Optional<String> r0 = courseNullable.flatMap(e->e.getStu(0)).map(Student::getName);
Optional<String> r1 = courseNullable.flatMap(e->e.getStu(1)).map(Student::getName);
Optional<String> r2 = courseNullable.flatMap(e->e.getStu(2)).map(Student::getName);
Optional<String> r3 = courseNullable.flatMap(e->e.getStu(3)).map(Student::getName);
System.out.println(r0);
System.out.println(r1);//Student的name属性为null,返回Optional.empty()
System.out.println(r2);//Student为null,返回Optional.empty()
System.out.println(r3);//数组越界,返回Optional.empty()

输出如下:

Optional[s0]
Optional.empty
Optional.empty
Optional.empty

对比以前的代码

Optional<String> resultOptional = courseNullable.map(e->e.getStu(index)).map(e->e.getName());

map(e->e.getStu(0))改成了flatMap(e->e.getStu(0))。这是因为改造前的e->e.getStu(0)返回的是Student对象,

而改造后返回的是Optional<Student>类型对象。观察改造后的代码段:

Optional<Optional<Student>> xa = courseNullable.map(e->e.getStu(2));
Optional<Student> xb = courseNullable.flatMap(e->e.getStu(2));

可以看到xa并不是我们想要的结果,而xb才是我们想要的结果。前面已经提到Optional相当于一个容器,那么Optional<Optional<Student>>

相当于容器中嵌套一个容器,在这里我们需要关注的是大容器里面的Optional<Student>类型对象。flatMap中的flat可以理解为平坦化,相当于

从嵌套的容器中取出自己真正想要的元素。

Java8-Optional与null的更多相关文章

  1. Java8特性---关于Null

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/SJQ. http://www.cnblogs.com/shijiaqi1066/p/5713941.html ...

  2. Java8 Optional的简单操作

    我们经常会遇到这种情况:首先判断一个对象是否为null,如果不为null,获取一个对象中的一个属性,如果该属性不为null,又获取该属性的属性,如果该属性的属性不为null,又获取属性的属性的属性: ...

  3. java8 Optional使用总结

    [前言] java8新特性 java8 函数接口 java8 lambda表达式 Java 8 时间日期使用 java8 推出的Optional的目的就是为了杜绝空指针异常,帮助开发者开发出更优雅的代 ...

  4. java8 Optional优雅非空判断

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

  5. Java8 Optional && Guava Optional

    Java8 -- Optional boolean isPresent():与obj != null()一样:调用get()前要调用isPresent()检查,不然会报错 Optional的三种构造方 ...

  6. java代码之美(16) ---Java8 Optional

    Java8 Optional 一句话介绍Optional类:使用JDK8的Optional类来防止NullPointerException(空指针异常)问题. 一.前言 在我们开放过程中,碰到的异常中 ...

  7. JDK8新特性:使用Optional避免null导致的NullPointerException

    空指针异常是导致Java应用程序失败的最常见原因.以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写 ...

  8. java8 - Optional

    mport java.util.Optional; import org.junit.Test; /* * 一.Optional 容器类:用于尽量避免空指针异常 * Optional.of(T t) ...

  9. 详解Java8 Optional类{最全}

    1:Optional 1.1 概述 Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException),提供了一些的方法代替过去的if-else处理逻辑,并与Stre ...

  10. Java8——Optional

    /* * 一.Optional 容器类:用于尽量避免空指针异常 * Optional.of(T t) : 创建一个 Optional 实例 * Optional.empty() : 创建一个空的 Op ...

随机推荐

  1. elasticsearch 的内存JVM和GC相关问题

    JVM对ElasticSearch集群的稳定性有很大的影响. Java是一个垃圾收集语言,意思是这个程序不会手动管理分配和释放内存.程序员只需要编写代码,jvm管理根据需要管理分配内存的处理,然后在不 ...

  2. 马凯军201771010116《面向对象与程序设计Java》第十周学习总结

    一.理论知识学习部分 泛型类的约束与局限性: 不能用基本类型实例化类型参数 运行时类型查询只适用于原始类型 不能抛出也不能捕获泛型类实例 参数化类型的数组不合法 不能实例化类型变量 泛型类的 ...

  3. django遇到的问题-系列1

    django开发中遇到的问题以及解决方法: 1.You called this URL via POST, but the URL doesn't end in a slash and you hav ...

  4. Linux 常用命令和使用技巧

    一.Shell命令 shell通配符------"*"."?"."[]" shell管道-------|把命令连接起来,把第一个命令的输出作 ...

  5. wpf binging(四) 不明确的目标绑定

    并不是每个控件都被设置了 Name 属性 ,有时候 我们只知道 他与某个控件的层级关系, 示例: AncestorType:类型代表要寻找的控件的类型 Ancestprlevel:偏移量 ,比如 1 ...

  6. LCA(tarjan)

    这是LCA算法中的一种,Tarjan算法 其实这么说也有点不对,应该是Tarjan+DFS进行解决 LCA又称为最近公共祖先 那么什么是最近公共祖先: 在一棵没有环的树上,每个节点肯定有其父亲节点和祖 ...

  7. Laravel写讯搜的ini配置文件的一处陷阱

    讯搜客户端在连接服务端之前需要读取配置文件里的配置信息. 配置文件里, 我一开始这样写: server.index = 127.0.0.1:8383 ; 表示索引接口 server.search = ...

  8. Delphi编程之爬取贴吧图片最终版

    接着前面两篇文章的内容,我们今天把这个贴吧爬取图片的程序完善,让它具有可以下载贴吧多页和帖子多页图片的能力. 主界面设计如下,包含3个labelededit,3个button,1个memo,1个str ...

  9. Centos解除端口占用

    - 查看所有端口占用 - netstat -tln - 查看端口被哪个进程占用 - lsof -i:端口号 - 杀死被占用端口 - kill 端口号

  10. Eureka的初识

    在bili看完spring cloud eureka高可用注册中心的视频以后总结: 正常开发中,肯定有一个功能聚集服务中心,将功能方法全部写入其中,也就是一个springboot项目.该服务配置如下: ...