聊一聊Java8 Optional,讓你的程式碼更加優雅

渣男小四發表於2022-03-31

碼農在囧途

隨著時間的推移,曾經我們覺得重要的東西,可能在今天看來是如此的淺薄和無知,同理,今天我們放不下,想不開,覺得重要的東西,多年後我們可能也會覺得也就那樣,所以,今天的的所有煩惱,憂愁,想不開,其實我們都沒必要過於在意,因為有些東西隨著時間的沖刷,也就那樣了。

前言

Java8提供了Optional介面,Optional介面能夠是我們的程式碼變得更加的優雅,可讀性更高,同時能夠很好的避免空指標,因為空指標是一個很讓人頭疼的問題,特別對於呼叫第三方介面,如果不知道物件的規約的時候,我們在取值的時候無法直到那些值能為空,那些不能為空,所以容易出現空指標,如果我們謹慎一點,可能會對每一個值進行判空處理,但是將會充斥著大量的if語句,甚是不雅觀。

下面我們介紹一下Optional類的方法

empty()

返回一個空的Optional物件 Optional.empty

of(T value)

引數傳入一個物件,返回一個Option物件,value不能為空,如果為null,將丟擲空指標異常

 /**
  * @author 劉牌
  * @date 2022-03-2921:52
  */
 public class OptionalTest {
     public static void main(String[] args) {
         User user = null;
         Optional<User> optional = Optional.of(user);
         System.out.println(user);
     }
 }

ofNullable(T value)

引數傳入一個物件,可以為空,如果為空,將返回一個空的Optional物件,就等於Optional.empty(),輸出的值為Optional.empty,如果不為空,返回一個不為空的Optional物件

 /**
  * @author 劉牌
  * @date 2022-03-2921:52
  */
 public class OptionalTest {
     public static void main(String[] args) {
         User user = null;
         Optional<User> optional = Optional.ofNullable(user);
         System.out.println(optional);
     }
 }

get()

獲取Optional中的值,這個值也就是我們的值,Optional相當於就是一個外殼。

 public class OptionalTest {
     public static void main(String[] args) {
         User user = null;
         Optional<User> optional = Optional.ofNullable(user);
         User user1 = optional.get();
     }
 }

isPresent()

判斷Optional物件中是否有值,如果有值,返回true,沒值返回false。

true

 public class OptionalTest {
     public static void main(String[] args) {
         User user = new User();
         Optional<User> optional = Optional.ofNullable(user);
         System.out.println(optional.isPresent());
     }
 }

false

 public class OptionalTest {
     public static void main(String[] args) {
         User user = null;
         Optional<User> optional = Optional.ofNullable(user);
         System.out.println(optional.isPresent());
     }
 }

ifPresent(Consumer<? super T> consumer)

ifPresent引數是一個函式式介面,無返回值,會將Optional中的值作為引數傳遞到ifPresent()中

 public class OptionalTest {
     public static void main(String[] args) {
         User user = new User();
         Optional<User> optional = Optional.ofNullable(user);
         optional.ifPresent(s -> System.out.println(s));
     }
 }

filter(Predicate<? super T> predicate)

是一個Predicate函式介面,會將Optional中的值作為引數傳入,如果符合規則,那麼返回一個Optional物件,否則返回一個空的Optional 物件(Optional.empty) 符合規則,返回Optional物件

 public class OptionalTest {
     public static void main(String[] args) {
         User user = new User();
         user.setUsername("我是小四哥");
         Optional<User> optional = Optional.ofNullable(user);
         Optional<User> optional1 = optional.filter(v -> v.getUsername().equals("我是小四哥"));
         System.out.println(optional1);
     }
 }

不符合規則,返回空Optional(Optional.empty)

 public class OptionalTest {
     public static void main(String[] args) {
         User user = new User();
         user.setUsername("我是小四哥");
         Optional<User> optional = Optional.ofNullable(user);
         Optional<User> optional1 = optional.filter(v -> v.getUsername().equals("我不是小四哥"));
         System.out.println(optional1);
     }
 }

map(Function<? super T, ? extends U> mapper)

引數是一個Function函式式介面,會將Optional中的值作為引數傳遞到map中,如果傳入的值為空,則返回一空的Optional物件,相當於Optional.empty(), 如果不為空,我們可以返回一個可以描述描述結果的返回值(Optional中的值,這個值可以重新賦值),如下面的返回一個tempUser

Optional中的值為空

 public class OptionalTest {
     public static void main(String[] args) {
         User user = null;
         Optional<String> optional = Optional.ofNullable(user).map(OptionalTest::getMap);
         System.out.println(optional); //Optional.empty
     }
 ​
     public static String getMap(User user){
         return user.getUsername();
     }
 }

Optional中的值不為空

 public class OptionalTest {
     public static void main(String[] args) {
         User user = new User();
         user.setUsername("我是小四哥");
         user.setAge(20);
         Optional<String> optional = Optional.ofNullable(user).map(OptionalTest::getMap);
         System.out.println(optional); // Optional[我是小四哥]
     }
 ​
     public static String getMap(User user){
         return user.getUsername();
     }
 }

flatMap(Function<? super T, Optional> mapper)

如果Optional中值存在,那麼返回一個基於Optional的值(如Optional),如果Optional中的值不存在,則返回一空的Optional物件,相當於Optional.empty(),與map不同, map返回的是一個值,而flatMap返回一個基於Optional的值

 public class OptionalTest {
     public static void main(String[] args) {
         User user = new User();
         user.setUsername("我是小四哥");
         user.setAge(20);
         Optional<String> optional = Optional.ofNullable(user).flatMap(OptionalTest::getFlatMap);
         System.out.println(optional); 
     }
 ​
     public static Optional<String> getFlatMap(User user){
         return Optional.ofNullable(user).map(User::getUsername);
     }
 }

orElse(T other)

如果Optional中的值不為空,則返回Optional中的值,如果為空,則返回other值,

 String value = "2";
 String orElse = Optional.ofNullable(value).orElse("1");  
 System.out.println(orElse);  //2  
         
 String value = null;
 String orElse = Optional.ofNullable(value).orElse("1"); 
 System.out.println(orElse);  //1

orElseGet(Supplier<? extends T> other)

如果Optional中存在值,則返回值,否則返回other呼叫的結果

Optional中存在值

 public class OptionalTest {
     public static void main(String[] args) {
         String value = null;
         String orElse = Optional.ofNullable(value).orElseGet(OptionalTest::get);
         System.out.println(orElse);  // 123
     }
 ​
     public static String get(){
         return "123";
     }
 }

Optional中不存在值

 public class OptionalTest {
     public static void main(String[] args) {
         String value = "2";
         String orElse = Optional.ofNullable(value).orElseGet(OptionalTest::get);
         System.out.println(orElse);  // 2
     }
 ​
     public static String get(){
         return "123";
     }
 }

orElseThrow(Supplier<? extends X> exceptionSupplier)

如果Optional中的值存在,則返回值,值不存在,則丟擲異常函式Supplier中的異常

 public class OptionalTest {
     public static void main(String[] args) {
         String value = null;
         String orElse = Optional.ofNullable(value).orElseThrow(() -> new RuntimeException("不存在值"));
         System.out.println(orElse);
     }
 }

輸出

 Exception in thread "main" java.lang.RuntimeException: 不存在值
     at com.steak.javase.optional.OptionalTest.lambda$main$0(OptionalTest.java:14)
     at java.util.Optional.orElseThrow(Optional.java:290)
     at com.steak.javase.optional.OptionalTest.main(OptionalTest.java:13)

實戰,去除if判斷

我們將一個從遠端介面獲取了一個使用者的資訊,包含家庭資訊,學歷資訊,個人資訊,然後封裝成一個VO,再返回前端進行展示。

家庭實體Family

 /**
  * @author 劉牌
  * @date 2022-03-3023:22
  */
 @Data
 @Accessors(chain = true)
 public class Family {
     private String fatherName;
     private String motherName;
 }

學歷實體Education

 @Data
 @Accessors(chain = true)
 public class Education {
     private String education;
     private String schoolName;
     private Date admissionTime;
     private Date graduationTime;
 }

使用者資訊實體UserInfo

 /**
  * @author 劉牌
  * @date 2022-03-3023:16
  */
 @Data
 @Accessors(chain = true)
 public class UserInfo {
     private String name;
     private Integer age;
     private Family family;
     private Education education;
 }

使用者VO,返回前端的檢視物件

 /**
  * @author 劉牌
  * @date 2022-03-2922:02
  */
 @Data
 @Accessors(chain = true)
 public class UserVO {
     private String username;
     private Integer userAge;
     private String edu;
     private String school;
     private Date admissionDate;
     private Date graduationDate;
     private String mother;
     private String father;
 }

如果沒有做判空處理,如下程式碼,因為getUser()介面中並沒有為family屬性設值,所以為null,那麼main方法中對其進行賦值的時候,肯定會丟擲空指標異常NullPointerException。

 /**
  * @author 劉牌
  * @date 2022-03-2921:52
  */
 public class OptionalTest {
     public static void main(String[] args) {
         UserInfo userInfo = getUser();
         UserVO user = new UserVO();
         user.setUsername(userInfo.getName()).setUserAge(userInfo.getAge())
                 .setEdu(userInfo.getEducation().getEducation())
                 .setSchool(userInfo.getEducation().getSchoolName())
                 .setAdmissionDate(userInfo.getEducation().getAdmissionTime())
                 .setGraduationDate(userInfo.getEducation().getGraduationTime())
                 .setFather(userInfo.getFamily().getFatherName())
                 .setMother(userInfo.getFamily().getMotherName());
         System.out.println(user);
     }
 ​
     public static UserInfo getUser(){
         Family family = new Family().setFatherName("father")
                 .setMotherName("mother");
         Education education = new Education().setEducation("本科")
                 .setSchoolName("家裡蹲大學")
                 .setAdmissionTime(new Date())
                 .setGraduationTime(new Date());
         return new UserInfo()
                 .setName("小四哥")
                 .setAge(24)
                 .setEducation(education);
     }
 }

所以我們需要做判空校驗,那麼大多數人肯定會使用如下方式進行判斷,先判斷userInfo是否null,再接著判斷education屬性和family屬性是否為null,為null不賦值,這樣就能避免空指標,這是絕大多數人都會這樣做的,這樣做確實是沒錯的,但是我們發現程式碼中存在很多if判空操作,看起來其實不怎麼好看。

 if (null != userInfo){
     user.setUsername(userInfo.getName()).setUserAge(userInfo.getAge());
     if (null != userInfo.getEducation()){
         user.setEdu(userInfo.getEducation().getEducation())
                 .setSchool(userInfo.getEducation().getSchoolName())
                 .setAdmissionDate(userInfo.getEducation().getAdmissionTime())
                 .setGraduationDate(userInfo.getEducation().getGraduationTime());
     }
     if (null != userInfo.getFamily()){
         user.setFather(userInfo.getFamily().getFatherName())
                 .setMother(userInfo.getFamily().getMotherName());
     }
 }

使用Optional介面進行改造,額~~~,這好像也沒改造出啥東西來啊,沒錯,確實沒改造出什麼新花樣來,除了增加一些函式操作,基本沒啥改變,但是我們能直觀的看出,程式碼比上面的會好維護一點,因為大家都比較煩if,那麼,除了好看一點,好維護一點,還有啥好處呢,我覺得如果你對一些返回的結果不能很確定其返回值值的情況下,那麼使用Optional來避免空指標是一個很好的辦法,因為在開發中可能並不會想得那麼周到,可能某處因為疏忽或者沒考慮到,忘記加了if判空,那麼後續可能會導致空指標,如果使用Optional的話,那麼這個問題能夠得到避免。

 Optional.ofNullable(userInfo).ifPresent(userInfoI -> {
     user.setUsername(userInfoI.getName()).setUserAge(userInfoI.getAge());
     Optional.ofNullable(userInfoI.getFamily()).ifPresent(family -> {
         user.setFather(userInfo.getFamily().getFatherName()).setMother(userInfo.getFamily().getMotherName());
     });
     Optional.ofNullable(userInfoI.getEducation()).ifPresent(education -> {
         user.setEdu(userInfo.getEducation().getEducation())
                 .setSchool(userInfo.getEducation().getSchoolName())
                 .setAdmissionDate(userInfo.getEducation().getAdmissionTime())
                 .setGraduationDate(userInfo.getEducation().getGraduationTime());
     });
 });

獲取使用者畢業時間,去除if多重判斷

不使用Optional,使用if判斷

 public static Date getGraduationTime(UserInfo userInfo){
     if (null != userInfo){
         if (null != userInfo.getEducation()){
             return userInfo.getEducation().getGraduationTime();
         }
     }
     return null;
 }

使用Optional

 public static Date getGraduationTime(UserInfo userInfo){
         return Optional.ofNullable(userInfo)
                 .map(UserInfo::getEducation)
                 .map(Education::getGraduationTime)
                 .orElse(null);
 }

關於Optional,裡面還有很多好用的方法和操作,我們就不展開了。

今天的分享就到這裡,感謝你的觀看,我們下期見!

相關文章