碼農在囧途
隨著時間的推移,曾經我們覺得重要的東西,可能在今天看來是如此的淺薄和無知,同理,今天我們放不下,想不開,覺得重要的東西,多年後我們可能也會覺得也就那樣,所以,今天的的所有煩惱,憂愁,想不開,其實我們都沒必要過於在意,因為有些東西隨著時間的沖刷,也就那樣了。
前言
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,裡面還有很多好用的方法和操作,我們就不展開了。
今天的分享就到這裡,感謝你的觀看,我們下期見!