Java的函數式接口編程示例
背景
函數式接口是在Java 8中引入的,與Lambda表達式和方法引用一起。這三個特性被添加到Java中,以促進函數式編程并編寫干凈、可讀的代碼。在Java 8之前,需要編寫大量樣板代碼來涵蓋基本功能。例如,為了調用一個函數,首先我們必須創建一個具有所需方法的類,創建類的實例,然后使用實例來調用方法,或者使用具有相應方法的匿名類的另一種方式。
使用Lambda表達式,我們可以避免使用具體類和匿名類對象的需求。函數式接口更進一步,因為Lambda表達式只需要實現一個方法,所以可以輕松地實現函數式接口。
函數式接口只展示一個功能。例如,一個具有單一方法compareTo()的Comparable接口用于比較目的。但它可以有任意數量的默認方法和靜態方法。
Java 8定義了許多函數式接口,可以廣泛用于Lambda表達式。以下是在java.util.Function包中定義的函數式接口列表。
@FunctionalInterface注解,按照功能,任何具有單個抽象方法的接口都是函數式接口。Java提供了@FunctionalInterface注解,以將一個接口標記為函數式接口,以便編譯器可以檢查接口是否是函數式接口。此注解是可選的,主要是為了增加編譯器的檢查和增加代碼的可讀性和維護性。
函數式接口編程示例
函數式接口的類型:在Java中,主要有四種類型的函數式接口。
Predicate函數式接口:Predicate函數式接口是一種方法接受一個參數,并返回true或false的接口。Predicate函數式接口主要用于比較元素以進行排序或根據應用于傳入的輸入的某些條件來過濾值。Java提供了用于基本類型的Predicate函數式接口,如IntPredicate、DoublePredicate和LongPredicate,分別只接受Integer、Double和Long類型的參數。
用法:
Predicate predicate = (value) -> value != 0; //或者
Predicate predicate = (value) -> test(value);
在上面的代碼片段中,predicate函數根據傳入的值返回true或false。
示例:
在這個例子中,我們使用predicate函數式接口和lambda表達式來從一個整數列表中過濾出奇數。
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);
}
}
編譯運行上述程序后,輸出結果為:
[2, 4, 6, 8]
Consumer函數式接口:Consumer函數式接口是一種方法接受一個參數,并且不返回任何值的接口。Consumer函數式接口主要用于執行副作用操作。例如,打印一個元素、添加稱謂等。還有其他變種的Consumer,比如BiConsumer。BiConsumer函數式接口可以接受兩個參數。Java提供了用于基本類型的Consumer函數式接口,如IntConsumer、DoubleConsumer和LongConsumer,分別只接受Integer、Double和Long類型的參數。
用法:
//定義
Consumer consumer = (value) -> System.out.println(value);
// 或者
Consumer consumer1 = System.out::println;
// 使用
Consumer consumer2 = (value) -> accept(value);
示例:
在這個例子中,我們借助lambda表達式和方法引用,使用consumer函數式接口來打印整數列表中的所有數字。
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);
}
}
編譯運行程序結果如下:
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函數式接口:Supplier函數式接口是一種沒有任何參數傳遞且會返回一個值的接口。Supplier函數式接口主要用于延遲生成值。例如,獲取一個隨機數,生成一系列數字等。
用法:
//定義
Supplier supplier = () -> Math.random() * 10;
// 使用
Supplier supplier1 = () -> get();
示例:
在這個例子中,我們借助lambda表達式,使用Supplier函數式接口來獲取一個隨機數。
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);
}
}
編譯并運行程序,得出運行結果:
[0, 8, 8, 8, 8, 5, 7, 5, 5, 9]
Function函數式接口:Function函數式接口是一種方法接受一個參數并返回一個值的接口。Function函數式接口主要用于獲取處理后的值。例如,獲取一個元素的平方,修剪字符串值等。還有其他的Function變體,比如BiFunction。BiFunction函數式接口可以接受兩個參數。Java還提供了針對基本類型的Function函數式接口,如IntFunction、DoubleFunction和LongFunction,分別只接受Integer、Double和Long類型的參數。還有兩個更實用的接口,UnaryOperator擴展了Function接口,BinaryOperator擴展了BiFunction接口。
用法:
//定義
Function function = (value) -> Math.random() * 10;
// 使用
Function function1 = (value) -> apply(value);
示例:
在這個例子中,我們借助lambda表達式,使用Function函數式接口來獲取一個平方數。
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);
}
}
編譯并運行程序,得出運行結果:
[1, 4, 9, 16, 25, 36, 49, 64]
注意事項:
在Java 8之前,已經存在的許多接口被注釋為函數式接口,并可以在lambda表達式中使用。例如:
- Runnable ?提供run() 方法
- Callable ? 提供 call() 方法
- Actionlistener ? 提供actionPerformed() 方法
- Comparable ? 提供 compareTo() 方法比較兩個數的大小
示例:
在這個例子中,我們創建了兩個線程。第一個線程使用匿名類創建,第二個線程使用lambda表達式創建。兩者都使用runnable接口來創建線程實例。
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();
}
}
編譯并運行程序,結果:
Thread 1 is running
Thread 2 is running.