Java的函數(shù)式接口編程示例
背景
函數(shù)式接口是在Java 8中引入的,與Lambda表達(dá)式和方法引用一起。這三個(gè)特性被添加到Java中,以促進(jìn)函數(shù)式編程并編寫干凈、可讀的代碼。在Java 8之前,需要編寫大量樣板代碼來(lái)涵蓋基本功能。例如,為了調(diào)用一個(gè)函數(shù),首先我們必須創(chuàng)建一個(gè)具有所需方法的類,創(chuàng)建類的實(shí)例,然后使用實(shí)例來(lái)調(diào)用方法,或者使用具有相應(yīng)方法的匿名類的另一種方式。
使用Lambda表達(dá)式,我們可以避免使用具體類和匿名類對(duì)象的需求。函數(shù)式接口更進(jìn)一步,因?yàn)長(zhǎng)ambda表達(dá)式只需要實(shí)現(xiàn)一個(gè)方法,所以可以輕松地實(shí)現(xiàn)函數(shù)式接口。
函數(shù)式接口只展示一個(gè)功能。例如,一個(gè)具有單一方法compareTo()的Comparable接口用于比較目的。但它可以有任意數(shù)量的默認(rèn)方法和靜態(tài)方法。
Java 8定義了許多函數(shù)式接口,可以廣泛用于Lambda表達(dá)式。以下是在java.util.Function包中定義的函數(shù)式接口列表。
@FunctionalInterface注解,按照功能,任何具有單個(gè)抽象方法的接口都是函數(shù)式接口。Java提供了@FunctionalInterface注解,以將一個(gè)接口標(biāo)記為函數(shù)式接口,以便編譯器可以檢查接口是否是函數(shù)式接口。此注解是可選的,主要是為了增加編譯器的檢查和增加代碼的可讀性和維護(hù)性。
函數(shù)式接口編程示例
函數(shù)式接口的類型:在Java中,主要有四種類型的函數(shù)式接口。
Predicate函數(shù)式接口:Predicate函數(shù)式接口是一種方法接受一個(gè)參數(shù),并返回true或false的接口。Predicate函數(shù)式接口主要用于比較元素以進(jìn)行排序或根據(jù)應(yīng)用于傳入的輸入的某些條件來(lái)過(guò)濾值。Java提供了用于基本類型的Predicate函數(shù)式接口,如IntPredicate、DoublePredicate和LongPredicate,分別只接受Integer、Double和Long類型的參數(shù)。
用法:
Predicate predicate = (value) -> value != 0; //或者
Predicate predicate = (value) -> test(value);
在上面的代碼片段中,predicate函數(shù)根據(jù)傳入的值返回true或false。
示例:
在這個(gè)例子中,我們使用predicate函數(shù)式接口和lambda表達(dá)式來(lái)從一個(gè)整數(shù)列表中過(guò)濾出奇數(shù)。
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class Tester {
public static void main(String args[]) {
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8);
Predicate<Integer> isEvenNumber = n -> n %2 == 0;
numbers = numbers.stream().filter(isEvenNumber).toList();
System.out.println(numbers);
}
}
編譯運(yùn)行上述程序后,輸出結(jié)果為:
[2, 4, 6, 8]
Consumer函數(shù)式接口:Consumer函數(shù)式接口是一種方法接受一個(gè)參數(shù),并且不返回任何值的接口。Consumer函數(shù)式接口主要用于執(zhí)行副作用操作。例如,打印一個(gè)元素、添加稱謂等。還有其他變種的Consumer,比如BiConsumer。BiConsumer函數(shù)式接口可以接受兩個(gè)參數(shù)。Java提供了用于基本類型的Consumer函數(shù)式接口,如IntConsumer、DoubleConsumer和LongConsumer,分別只接受Integer、Double和Long類型的參數(shù)。
用法:
//定義
Consumer consumer = (value) -> System.out.println(value);
// 或者
Consumer consumer1 = System.out::println;
// 使用
Consumer consumer2 = (value) -> accept(value);
示例:
在這個(gè)例子中,我們借助lambda表達(dá)式和方法引用,使用consumer函數(shù)式接口來(lái)打印整數(shù)列表中的所有數(shù)字。
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class Tester {
public static void main(String args[]) {
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8);
Consumer<Integer> consumer = (value) -> System.out.println(value);
Consumer consumer1 = System.out::println;
System.out.println("Printing using consumer functional interface as lambda expression");
numbers.forEach(consumer);
System.out.println("Printing using consumer functional interface as method reference");
numbers.forEach(consumer1);
}
}
編譯運(yùn)行程序結(jié)果如下:
Printing using consumer functional interface as lambda expression
1
2
3
4
5
6
7
8
Printing using consumer functional interface as method reference
1
2
3
4
5
6
7
8
Supplier函數(shù)式接口:Supplier函數(shù)式接口是一種沒(méi)有任何參數(shù)傳遞且會(huì)返回一個(gè)值的接口。Supplier函數(shù)式接口主要用于延遲生成值。例如,獲取一個(gè)隨機(jī)數(shù),生成一系列數(shù)字等。
用法:
//定義
Supplier supplier = () -> Math.random() * 10;
// 使用
Supplier supplier1 = () -> get();
示例:
在這個(gè)例子中,我們借助lambda表達(dá)式,使用Supplier函數(shù)式接口來(lái)獲取一個(gè)隨機(jī)數(shù)。
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public class Tester {
public static void main(String args[]) {
Supplier<Integer> supplier = () -> (int)(Math.random() * 10);
List<Integer> randomNumbers = new ArrayList<>();
// generate 10 random numbers
for(int i = 0; i< 10; i++) {
randomNumbers.add(supplier.get());
}
System.out.println(randomNumbers);
}
}
編譯并運(yùn)行程序,得出運(yùn)行結(jié)果:
[0, 8, 8, 8, 8, 5, 7, 5, 5, 9]
Function函數(shù)式接口:Function函數(shù)式接口是一種方法接受一個(gè)參數(shù)并返回一個(gè)值的接口。Function函數(shù)式接口主要用于獲取處理后的值。例如,獲取一個(gè)元素的平方,修剪字符串值等。還有其他的Function變體,比如BiFunction。BiFunction函數(shù)式接口可以接受兩個(gè)參數(shù)。Java還提供了針對(duì)基本類型的Function函數(shù)式接口,如IntFunction、DoubleFunction和LongFunction,分別只接受Integer、Double和Long類型的參數(shù)。還有兩個(gè)更實(shí)用的接口,UnaryOperator擴(kuò)展了Function接口,BinaryOperator擴(kuò)展了BiFunction接口。
用法:
//定義
Function function = (value) -> Math.random() * 10;
// 使用
Function function1 = (value) -> apply(value);
示例:
在這個(gè)例子中,我們借助lambda表達(dá)式,使用Function函數(shù)式接口來(lái)獲取一個(gè)平方數(shù)。
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
public class Tester {
public static void main(String args[]) {
List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8);
Function<Integer, Integer> squared = (value) -> value * value;
List<Integer> squaredNumbers = numbers.stream().map(squared).toList();
System.out.println(squaredNumbers);
}
}
編譯并運(yùn)行程序,得出運(yùn)行結(jié)果:
[1, 4, 9, 16, 25, 36, 49, 64]
注意事項(xiàng):
在Java 8之前,已經(jīng)存在的許多接口被注釋為函數(shù)式接口,并可以在lambda表達(dá)式中使用。例如:
- Runnable ?提供run() 方法
- Callable ? 提供 call() 方法
- Actionlistener ? 提供actionPerformed() 方法
- Comparable ? 提供 compareTo() 方法比較兩個(gè)數(shù)的大小
示例:
在這個(gè)例子中,我們創(chuàng)建了兩個(gè)線程。第一個(gè)線程使用匿名類創(chuàng)建,第二個(gè)線程使用lambda表達(dá)式創(chuàng)建。兩者都使用runnable接口來(lái)創(chuàng)建線程實(shí)例。
public class Tester {
public static void main(String args[]) {
// create anonymous inner class object
new Thread(new Runnable() {
@Override public void run() {
System.out.println("Thread 1 is running");
}
}).start();
// lambda expression to create the object
new Thread(() -> {
System.out.println("Thread 2 is running.");
}).start();
}
}
編譯并運(yùn)行程序,結(jié)果:
Thread 1 is running
Thread 2 is running.