程序中常常須要對null狀況進行處理,好比Course類中有一個List
如今想要查看某個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進行判斷。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);
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中的其餘方法ifPresent
與orElse
oracle
stu.ifPresent(e->System.out.println(e.getName()));//若是存在對象,則Optional中的包含的對象getName返回的值 Student defaultStudent = new Student("默認對象"); Student orElse = stu.orElse(defaultStudent); //若是存在對象直接返回該對象,不然返回defaultStudent對象
彷佛無補於事。ide
對上面的代碼可使用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<>(); } }
查看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); }
該段代碼主要有兩個問題:
改造後代碼以下:
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能夠理解爲平坦化,至關於 從嵌套的容器中取出本身真正想要的元素。