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能夠理解爲平坦化,至關於 從嵌套的容器中取出本身真正想要的元素。

相關文章
相關標籤/搜索