Java8之Consumer、Supplier、Predicate和Function攻略
參考資料:(https://kknews.cc/zh-tw/code/jl4ba6q.html)
這幾個接口都在 java.util.function 包下的,分別是Consumer(消費型)、supplier(供給型)、predicate(謂詞型)、function(功能性),相信有了後面的解釋,你應該非常清楚這個接口的功能了。
Consumer
接收參數,並"使用該參數"的一種型別
/**
* consumer接口測試
*/
@Test
public void test_Consumer() {
//① 使用consumer接口實現方法
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
Stream<String> stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
stream.forEach(consumer);
//② 使用lambda表達式,forEach方法需要的就是一個Consumer接口
System.out.println("********************");
stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
Consumer<String> consumer1 = (s) -> System.out.println(s);
//lambda表達式返回的就是一個Consumer接口
stream.forEach(consumer1);
// 更直接的方式
// stream.forEach((s) -> System.out.println(s));
System.out.println("********************");
//③ 使用方法引用,方法引用也是一個consumer
stream = Stream.of("aaa", "bbb", "ddd", "ccc", "fff");
Consumer consumer2 = System.out::println;
stream.forEach(consumer);
//更直接的方式
// stream.forEach(System.out::println);
}
Supplier
"提供某種參數"的型別、容器
範例一
/**
* Supplier接口測試,supplier相當一個容器或者變量,可以存儲值
*/
@Test
public void test_Supplier() {
//① 使用Supplier接口實現方法,只有一個get方法,無參數,返回一個值
Supplier<Integer> supplier = new Supplier<Integer>() {
@Override
public Integer get() {
//返回一個隨機值
return new Random().nextInt();
}
};
System.out.println(supplier.get());
System.out.println("********************");
//② 使用lambda表達式,
supplier = () -> new Random().nextInt();
System.out.println(supplier.get());
System.out.println("********************");
//③ 使用方法引用
Supplier<Double> supplier2 = Math::random;
System.out.println(supplier2.get());
}
範例二(提供給Optional)
/**
* Supplier接口測試2,使用需要Supplier的接口方法
*/
@Test
public void test_Supplier2() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
//返回一個optional對象
Optional<Integer> first = stream.filter(i -> i > 4).findFirst();
//optional對象有需要Supplier接口的方法
//orElse,如果first中存在數,就返回這個數,如果不存在,就放回傳入的數
System.out.println(first.orElse(1));
System.out.println(first.orElse(7));
System.out.println("********************");
Supplier<Integer> supplier = new Supplier<Integer>() {
@Override
public Integer get() {
//返回一個隨機值
return new Random().nextInt();
}
};
//orElseGet,如果first中存在數,就返回這個數,如果不存在,就返回supplier返回的值
System.out.println(first.orElseGet(supplier));
}
Predicate
判斷,提供一種"條件判斷"的一種方法
範例一
/**
* Predicate謂詞測試,謂詞其實就是一個判斷的作用類似bool的作用
*/
@Test
public void test_Predicate() {
//① 使用Predicate接口實現方法,只有一個test方法,傳入一個參數,返回一個bool值
Predicate<Integer> predicate = new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
if (integer > 5) {
return true;
}
return false;
}
};
System.out.println(predicate.test(6));
System.out.println("********************");
//② 使用lambda表達式
predicate = (t) -> t > 5;
System.out.println(predicate.test(1));
System.out.println("********************");
}
範例二
/**
* Predicate謂詞測試,Predicate作為接口使用
*/
@Test
public void test_Predicate2() {
//① 將Predicate作為filter接口,Predicate起到一個判斷的作用
Predicate<Integer> predicate = new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
if (integer > 5) {
return true;
}
return false;
}
};
Stream<Integer> stream = Stream.of(1, 23, 3, 4, 5, 56, 6, 6);
List<Integer> list = stream.filter(predicate).collect(Collectors.toList());
list.forEach(System.out::println);
System.out.println("********************");
// lambda 寫法
Predicate<Integer> predicate2 = integer -> {
return integer > 5;
};
Stream<Integer> stream2 = Stream.of(1, 23, 3, 4, 5, 56, 6, 6);
List<Integer> list2 = stream2.filter(predicate2).collect(Collectors.toList());
list2.forEach(System.out::println);
}
Function
功能型接口,將參數進行轉換的一種方法
範例一
/**
* Function測試,function的作用是轉換,將一個值轉為另外一個值
*/
@Test
public void test_Function() {
//① 使用map方法,泛型的第一個參數是轉換前的類型,第二個是轉化後的類型
Function<String, Integer> function = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
//獲取每個字符串的長度,並且返回
return s.length();
}
};
Stream<String> stream = Stream.of("aaa", "bbbbb", "ccccccv");
Stream<Integer> stream1 = stream.map(function);
stream1.forEach(System.out::println);
System.out.println("********************");
// lambda
Function<String, Integer> function2 = s -> {
//獲取每個字符串的長度,並且返回
return s.length();
};
Stream<String> stream2 = Stream.of("aaa", "bbbbb", "ccccccv");
Stream<Integer> stream3 = stream2.map(function2);
stream3.forEach(System.out::println);
System.out.println("********************");
}
額外參考資料:(https://matthung0807.blogspot.com/2018/09/java-8-javautilfunction-functional.html)
額外參考資料:(https://openhome.cc/Gossip/Java/ConsumerFunctionPredicateSupplier.html)
使用 Optional 取代 null
參考資料:(https://openhome.cc/Gossip/Java/Optional.html)
過去null太過被濫用,因為 null 的模糊不清、模稜兩可,導致 Java 工程師總是要跟NullPointerException奮戰。
null
的最根本問題在於語意含糊不清,雖然就字面來說,null
可以是「不存在」、「沒有」、「無」或「空」的概念,因此在應用時,總是令人感到模稜兩可,也就讓開發者有了各自解釋的空間,當開發者想到「嘿!這邊可以沒有東西…」就直接放個null
,或者是想到「嗯!沒什麼東西可以傳回…」,就不假思索地傳回個null
,然後使用者就總是忘了檢查null
,引發各種可能的錯誤。
原本會回傳null的寫法
static String getNickName(String name) {
Map<String, String> nickNames = new HashMap<>(); // 假裝的鍵值資料庫
nickNames.put("Justin", "caterpillar");
nickNames.put("Monica", "momor");
nickNames.put("Irene", "hamimi");
return nickNames.get(name); // 鍵不存在時會傳回null
}
在上面的程式中,如果呼叫getNickName()
時忘了檢查null
,那麼就會直接顯示null
,在這個簡單的例子中並不會怎樣,只是顯示結果令人困惑罷了,但如果後續的執行流程牽涉到至關重要的結果,程式快樂地繼續執行下去,錯誤可能到最後才會呈現發生。
改寫成回傳Optiona的寫法
static Optional<String> getNickName(String name) {
Map<String, String> nickNames = new HashMap<>();
nickNames.put("Justin", "caterpillar");
nickNames.put("Monica", "momor");
nickNames.put("Irene", "hamimi");
String nickName = nickNames.get(name);
return nickName == null ? Optional.empty() : Optional.of(nickName);
}
因為呼叫getNickName()
時傳回的是Optional
型態的實例,語義上表示它包含也可能不包括值,客戶端就要意識必須進行檢查,如果不檢查就直接呼叫Optional
的get()
方法:
String nickName = getNickName("Duke").get();
System.out.println(nickName);
在Optional
沒有包含值的情況下,就會直接拋出java.util.NoSuchElementException
,這實現了速錯(Fail fast)的概念,這讓開發者可以立即發現錯誤,並瞭解到必須使用程式碼作些檢查。
可能的方式之一像是:
Optional<String> nickOptional = getNickName("Duke");
String nickName = "Openhome Reader";
if(nickOptional.isPresent()) {
nickName = nickOptional.get();
}
System.out.println(nickName);
//或是寫成這樣
Optional<String> nickOptional = getNickName("Duke");
System.out.println(nickOptional.orElse("Openhome Reader"));