Java8-Optional与null

对null进行处理

程序中常常须要对null状况进行处理,好比Course类中有一个List stuList属性,Student有一个name属性。
如今想要查看某个student的name属性的长度,不负责任(不处理null)的写法以下:
html

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进行判断。java

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基本用法以下:api

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中的其余方法ifPresentorElseoracle

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

彷佛无补于事。ide

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处理。this

这个例子体现出了Optional的优越性,便可以经过其优雅的处理null值。
窃觉得,相比较Optioanl带来的优越性,其语法仍是有点复杂。建议先掌握其语法,这样至少看别人的相关代码的时候不会无所适从。code

基础代码

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的说明以下:htm

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能够理解为平坦化,至关于 从嵌套的容器中取出本身真正想要的元素。

相关文章
相关标签/搜索