Table of Contents
ToggleJava Optional Class
用來判斷空值,是一個容器物件,包含非空物件,可選物件用於表示沒有值的 null,具有各種實用方法,方便程式碼將值處理為可用或不可用,而不是檢查空值, Optional Class 介紹常見的 filter 、 map 、 flatMap 等方法,本篇增加了範例,並透過單元測試來驗證產出結果。
檔案目錄
./
+- src
+- test
| +- org
| +- ruoxue
| +- java_147
| +- optional
| +- OptionalClassTest.java
單元測試
Optional Class Java 提供過濾條件、轉換傳回 Optional 、轉換傳回物件等操作 Optional 。
filter
Optional Class Java 建立 Food 物件,覆寫 equals 、 hashCode ,建立 Optional 容器,依條件過濾容器內的值是否符合。
@NoArgsConstructor
@Getter
@Setter
public static class Food {
private String name;
private double quantity;
private int type;
private List<String> origins;
private Optional<String> id;
public Food(String name, double quantity, int type) {
this.name = name;
this.quantity = quantity;
this.type = type;
this.id = Optional.ofNullable(name).map(s -> s.toUpperCase());
}
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
builder.appendSuper(super.toString());
builder.append("name", name);
builder.append("quantity", quantity);
builder.append("type", type);
return builder.toString();
}
public boolean equals(Object object) {
if (!(object instanceof Food)) {
return false;
}
if (this == object) {
return true;
}
Food other = (Food) object;
return new EqualsBuilder().append(getName(), other.getName()).isEquals();
}
public int hashCode() {
return new HashCodeBuilder().append(getName()).toHashCode();
}
}
@Test
public void filter() {
Optional<String> opt = Optional.ofNullable("Beef");
opt = opt.filter(s -> s.startsWith("B"));
System.out.println(opt);
assertTrue(opt.isPresent());
Food food = new Food("Chicken", 2, 1);
Optional<Food> foodOpt = Optional.ofNullable(food);
foodOpt = foodOpt.filter(o -> o.name.startsWith("B"));
System.out.println(foodOpt);
assertFalse(foodOpt.isPresent());
List<Food> foodList = Arrays.asList(new Food("Beef", 1, 1), new Food("Chicken", 2, 1), new Food("Duck", 3, 1));
Optional<List<Food>> foodListOpt = Optional.ofNullable(foodList);
Predicate<List<Food>> predicate = list -> list.stream().filter(e -> e.name.startsWith("A")).count() > 0;
foodListOpt = foodListOpt.filter(predicate);
System.out.println(foodListOpt);
assertFalse(foodListOpt.isPresent());
}
Optional[Beef]
Optional.empty
Optional.empty
map
Optional Class Java 建立 Optional 容器,轉換容器內的值,傳回 Optional。
@Test
public void map() {
Optional<String> opt = Optional.ofNullable("Beef");
Optional<Integer> intOpt = opt.map(String::length);
System.out.println(intOpt);
int result = intOpt.orElse(0);
System.out.println(result);
assertEquals(4, result);
Food food = new Food("Chicken", 2, 1);
Optional<Food> foodOpt = Optional.ofNullable(food);
Optional<String> stringOpt = foodOpt.map(o -> o.name).map(String::trim).map(s -> s.toUpperCase())
.filter(s -> s.contains("E"));
System.out.println(stringOpt);
String stringResult = stringOpt.orElseGet(() -> null);
System.out.println(stringResult);
assertNotNull(stringResult);
food = new Food("Duck", 3, 1);
foodOpt = Optional.ofNullable(food);
Optional<List<String>> listOpt = foodOpt.map(o -> o.origins);
System.out.println(listOpt);
List<String> listResult = listOpt.orElseGet(ArrayList::new);
System.out.println(listResult);
assertNotNull(listResult);
}
Optional[4]
4
Optional[CHICKEN]
CHICKEN
Optional.empty
[]
flatMap
Optional Class Java 建立 Optional 容器,轉換容器內的值,傳回物件。
@Test
public void flatMap() {
Food food = new Food("Beef", 1, 1);
Optional<Food> foodOpt = Optional.ofNullable(food);
Optional<String> stringOpt = foodOpt.flatMap(o -> o.id);
System.out.println(stringOpt);
String stringResult = stringOpt.orElseGet(() -> null);
System.out.println(stringResult);
assertNotNull(stringResult);
}
Optional[BEEF]
BEEF
findWithTraditional
Optional Class in Java 傳統方法寫法,執行方法取得值,若值為 null 則拋出例外。
public static String findWithTraditional(String id) {
if (id != null) {
return id;
} else {
throw new IllegalArgumentException(id);
}
}
@Test(expected = IllegalArgumentException.class)
public void findWithTraditional() {
String result = findWithTraditional("Beef");
System.out.println(result);
assertEquals("Beef", result);
result = findWithTraditional(null);
System.out.println(result);
assertNull(result);
}
java.lang.IllegalArgumentException
at org.ruoxue.java_147.optional.OptionalClassTest.findWithTraditional(OptionalClassTest.java:129)
at org.ruoxue.java_147.optional.OptionalClassTest.findWithTraditional(OptionalClassTest.java:139)
findWithIsPresent
Optional Class in Java 使用 Optional isPresent 方法寫法,執行方法取得值,若值為 null 則拋出例外,與傳統寫法一樣,程式碼較不具可讀性。
public static String findWithIsPresent(String id) {
Optional<String> opt = Optional.ofNullable(id);
if (opt.isPresent()) {
return opt.get();
} else {
throw new IllegalArgumentException(id);
}
}
@Test(expected = IllegalArgumentException.class)
public void findWithIsPresent() {
String result = findWithIsPresent("Beef");
System.out.println(result);
assertEquals("Beef", result);
result = findWithIsPresent(null);
System.out.println(result);
assertNull(result);
}
java.lang.IllegalArgumentException
at org.ruoxue.java_147.optional.OptionalClassTest.findWithIsPresent(OptionalClassTest.java:149)
at org.ruoxue.java_147.optional.OptionalClassTest.findWithIsPresent(OptionalClassTest.java:159)
findWithOrElseThrow
Optional Class in Java 使用 Optional orElseThrow 方法寫法,執行方法取得值,若值為 null 則拋出例外,程式碼簡潔,較具可讀性。
public static String findWithOrElseThrow(String id) {
Optional<String> opt = Optional.ofNullable(id);
return opt.orElseThrow(() -> new IllegalArgumentException(id));
}
@Test(expected = IllegalArgumentException.class)
public void findWithOrElseThrow() {
String result = findWithOrElseThrow("Beef");
System.out.println(result);
assertEquals("Beef", result);
result = findWithOrElseThrow(null);
System.out.println(result);
assertNull(result);
}
java.lang.IllegalArgumentException
at org.ruoxue.java_147.optional.OptionalClassTest.lambda$14(OptionalClassTest.java:166)
at java.util.Optional.orElseThrow(Optional.java:290)
at org.ruoxue.java_147.optional.OptionalClassTest.findWithOrElseThrow(OptionalClassTest.java:166)
at org.ruoxue.java_147.optional.OptionalClassTest.findWithOrElseThrow(OptionalClassTest.java:175)
OptionalClassTest.java
Optional Class in Java 新增單元測試,驗證 Java Optional Class Example 是否符合預期。
package org.ruoxue.java_147.optional;
import static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.junit.Test;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
public class OptionalClassTest {
@NoArgsConstructor
@Getter
@Setter
public static class Food {
private String name;
private double quantity;
private int type;
private List<String> origins;
private Optional<String> id;
public Food(String name, double quantity, int type) {
this.name = name;
this.quantity = quantity;
this.type = type;
this.id = Optional.ofNullable(name).map(s -> s.toUpperCase());
}
public String toString() {
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
builder.appendSuper(super.toString());
builder.append("name", name);
builder.append("quantity", quantity);
builder.append("type", type);
return builder.toString();
}
public boolean equals(Object object) {
if (!(object instanceof Food)) {
return false;
}
if (this == object) {
return true;
}
Food other = (Food) object;
return new EqualsBuilder().append(getName(), other.getName()).isEquals();
}
public int hashCode() {
return new HashCodeBuilder().append(getName()).toHashCode();
}
}
@Test
public void filter() {
Optional<String> opt = Optional.ofNullable("Beef");
opt = opt.filter(s -> s.startsWith("B"));
System.out.println(opt);
assertTrue(opt.isPresent());
Food food = new Food("Chicken", 2, 1);
Optional<Food> foodOpt = Optional.ofNullable(food);
foodOpt = foodOpt.filter(o -> o.name.startsWith("B"));
System.out.println(foodOpt);
assertFalse(foodOpt.isPresent());
List<Food> foodList = Arrays.asList(new Food("Beef", 1, 1), new Food("Chicken", 2, 1), new Food("Duck", 3, 1));
Optional<List<Food>> foodListOpt = Optional.ofNullable(foodList);
Predicate<List<Food>> predicate = list -> list.stream().filter(e -> e.name.startsWith("A")).count() > 0;
foodListOpt = foodListOpt.filter(predicate);
System.out.println(foodListOpt);
assertFalse(foodListOpt.isPresent());
}
@Test
public void map() {
Optional<String> opt = Optional.ofNullable("Beef");
Optional<Integer> intOpt = opt.map(String::length);
System.out.println(intOpt);
int result = intOpt.orElse(0);
System.out.println(result);
assertEquals(4, result);
Food food = new Food("Chicken", 2, 1);
Optional<Food> foodOpt = Optional.ofNullable(food);
Optional<String> stringOpt = foodOpt.map(o -> o.name).map(String::trim).map(s -> s.toUpperCase())
.filter(s -> s.contains("E"));
System.out.println(stringOpt);
String stringResult = stringOpt.orElseGet(() -> null);
System.out.println(stringResult);
assertNotNull(stringResult);
food = new Food("Duck", 3, 1);
foodOpt = Optional.ofNullable(food);
Optional<List<String>> listOpt = foodOpt.map(o -> o.origins);
System.out.println(listOpt);
List<String> listResult = listOpt.orElseGet(ArrayList::new);
System.out.println(listResult);
assertNotNull(listResult);
}
@Test
public void flatMap() {
Food food = new Food("Beef", 1, 1);
Optional<Food> foodOpt = Optional.ofNullable(food);
Optional<String> stringOpt = foodOpt.flatMap(o -> o.id);
System.out.println(stringOpt);
String stringResult = stringOpt.orElseGet(() -> null);
System.out.println(stringResult);
assertNotNull(stringResult);
}
public static String findWithTraditional(String id) {
if (id != null) {
return id;
} else {
throw new IllegalArgumentException(id);
}
}
@Test(expected = IllegalArgumentException.class)
public void findWithTraditional() {
String result = findWithTraditional("Beef");
System.out.println(result);
assertEquals("Beef", result);
result = findWithTraditional(null);
System.out.println(result);
assertNull(result);
}
public static String findWithIsPresent(String id) {
Optional<String> opt = Optional.ofNullable(id);
if (opt.isPresent()) {
return opt.get();
} else {
throw new IllegalArgumentException(id);
}
}
@Test(expected = IllegalArgumentException.class)
public void findWithIsPresent() {
String result = findWithIsPresent("Beef");
System.out.println(result);
assertEquals("Beef", result);
result = findWithIsPresent(null);
System.out.println(result);
assertNull(result);
}
public static String findWithOrElseThrow(String id) {
Optional<String> opt = Optional.ofNullable(id);
return opt.orElseThrow(() -> new IllegalArgumentException(id));
}
@Test(expected = IllegalArgumentException.class)
public void findWithOrElseThrow() {
String result = findWithOrElseThrow("Beef");
System.out.println(result);
assertEquals("Beef", result);
result = findWithOrElseThrow(null);
System.out.println(result);
assertNull(result);
}
}
心得分享
Java Optional Class Example 是一個容器物件,它可能包含也可能不包含非空值,提供了依賴於所含值是否存在的方法, Optional Class in Java 在應用上相當廣泛,熟悉這些方法的操作,像是: filter 、 map 、 flatMap 等,提供了幾種 Optional 常見範例。