Java Optional Class - Java 147

Java Optional Class – Java 147

Java 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 常見範例。

發佈留言