背景
函数式接口是在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.