Java函数式接口FunctionInterface
一、简介
函数式接口是Java中的一个特殊接口类型,它只定义了一个抽象方法。尽管函数式接口中只能有一个抽象方法,但它可以有多个默认方法或静态方法。通过这种接口,Java支持将行为抽象为一个单独的函数,并能够通过lambda表达式或方法引用简洁地表示这些行为。
1.1 定义
@FunctionalInterface
public interface MyFunctionalInterface {
void execute();
}
在上述代码中,MyFunctionalInterface是一个函数式接口,因为它只有一个抽象方法execute()。@FunctionalInterface注解不是必须的,但加上这个注解可以显式声明这个接口是函数式接口,并在编译时提供检查机制。
1.2 作用
@FunctionalInterface的作用有以下几点:
- 编译器检查: 如果一个接口被
@FunctionalInterface注解标记,但包含多个抽象方法,那么编译器会报错。 - 自文档化: 使用
@FunctionalInterface注解可以让接口的意图更加明确,表明该接口是为了使用lambda表达式或方法引用设计的。
二、四大核心函数式接口
2.1 Function函数
Function接口是一个功能型接口,它的一个作用就是转换作用,将输入数据转换成另一种形式的输出数据。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Function<T, R> {
// 对给定的参数应用这个函数,并返回结果。
R apply(T t);
// 返回一个组合函数,首先将before函数应用于输入,然后将结果作为参数传递给当前函数。
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
// 返回一个组合函数,首先将当前函数应用于输入,然后将结果作为参数传递给after函数。
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
// 返回一个总是返回其输入参数的恒等函数。
static <T> Function<T, T> identity() {
return t -> t;
}
}
- apply:接受一个类型的参数,有返回值。功能是将参数赋予相应的方法。
- compose:先应用
beforefunction,再应用实例的function
实际上:将两个function组合在一起了。先执行before方法,然后将处理的结果传递给当前对象的apply方法。实现了两个function的串联。既然实现了两个function的串联,就能实现多个函数的串联。 - andThen: 和
before函数相反,先应用this function,然后再使用after方法。实现两个方法的串联。 - identity:传入什么,返回什么。
public class FunctionTest {
public static void main(String[] args) {
FunctionTest test = new FunctionTest();
// 传递行为,而不是传递值
System.out.println(test.comput(1, value -> 2 * value));
System.out.println(test.comput(2, value -> 5 + value));
System.out.println(test.comput(3, Integer::intValue));
System.out.println(test.convert(4, value -> value + "helloWorld"));
}
public int comput(int a, Function<Integer, Integer> function) {
//apply, 传递的是行为
int result = function.apply(a);
return result;
}
public String convert(int a, Function<Integer, String> function) {
return function.apply(a);
}
// 对于之前只传递值的写法,几种行为就要定义几种写法。现在可以使用上面的方式去传递行为
public int method1(int a) {
return a + 1;
}
public int method2(int a) {
return a * 5;
}
public int method3(int a) {
return a * a;
}
}
高阶函数:如果一个函数接收一个函数作为参数,或者返回一个函数作为返回值,那么该函数就叫做高阶函数。函数式编程语言js等语言里面都支持大量的高阶函数,JAVA从1.8开始也开始支持高阶函数。
/**
* compose, andThen方法的使用
*/
public class FunctionTest2 {
public static void main(String[] args) {
FunctionTest2 test2 = new FunctionTest2();
int compute = test2.compute(2, v -> v * 3, v -> v * v);//12
int compute2 = test2.compute2(2, v -> v * 3, v -> v * v);//36
System.out.println(compute);
System.out.println(compute2);
}
public int compute(int a, Function<Integer, Integer> function1,
Function<Integer, Integer> function2) {
return function1.compose(function2).apply(a);
}
public int compute2(int a, Function<Integer, Integer> function1,
Function<Integer, Integer> function2) {
return function1.andThen(function2).apply(a);
}
}
2.2 Consumer(消费者)
consumer接口就是一个消费型的接口,并且只要实现一个accept方法,就可以作为一个“消费者”输出信息。
其实,lambda表达式、方法引用的返回值都是Consumer类型,所以,他们能够作为forEach方法的参数,并且输出一个值。
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> {
accept(t);
after.accept(t);
};
}
}
- accept:意味消费一个指定泛型的数据。
public class ConsumerTest {
public static void test1() {
System.out.println("**********test1 start*****************");
List<String> list = Arrays.asList("hello", "world");
list.forEach(System.out::println);
// list.forEach(string -> System.out.println(string));
// list.forEach(new Consumer<String>() {
// @Override
// public void accept(String string) {
// System.out.println(string);
// }
// });
}
public static void test2() {
System.out.println("**********test2 start*****************");
List<String> list = new ArrayList<>();
Consumer<String> consumer = x -> {
if (x.startsWith("w")) {
list.add(x);
}
};
Stream.of("boy", "word", "cat", "way").forEach(consumer);
list.forEach(System.out::println);
}
public static void test3() {
System.out.println("**********test3 start*****************");
// 创建一个Consumer实例,打印输入的字符串
Consumer<String> printer = System.out::println;
// 使用accept方法来消费(使用)一个值
printer.accept("hello world!");
// 另一个使用Consumer的例子,这次是更新对象的某个字段的值
Consumer<Person> increaseAge = person -> person.setAge(person.getAge() + 1);
// 创建一个Person对象
Person person = new Person("张三", 30);
// 使用Consumer来增加年龄
increaseAge.accept(person);
// 打印出增加年龄后的结果
System.out.println(person.getName() + ": " + person.getAge());
}
public static void test4() {
System.out.println("**********test4 start*****************");
List<Person> list = new ArrayList<>();
list.add(new Person("张三", 30));
list.add(new Person("李四", 26));
list.add(new Person("王五", 28));
// 创建一个Consumer实例,添加
Consumer<Person> consumer = p -> {
if (p.getName().startsWith("张")) {
list.add(p);
}
};
consumer = consumer.andThen(
x -> list.removeIf(y -> y.getName().startsWith("李"))
);
list.forEach(System.out::println);
System.out.println("***************************");
Stream.of(new Person("张三", 18), new Person("王明", 32)).forEach(consumer);
list.forEach(System.out::println);
}
public static void main(String[] args) {
test1();
test2();
test3();
test4();
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
private String name;
private Integer age;
}
2.3 Predicate(谓词)
Predicate接口是一个谓词型接口,其实,这个就是通过实现一个test方法做判断
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate<T> {
// 在给定的参数上评估这个谓词
boolean test(T t);
// 返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑AND
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
// 返回表示此谓词的逻辑否定的谓词,相当于not
default Predicate<T> negate() {
return (t) -> !test(t);
}
// 返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑或
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
// 返回根据Objects.equals(Object, Object)测试两个参数是否相等的谓词
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
- test:给定一个输入参数,判断是否满足条件。满足则返回true,不满足返回false。
public class PredicateTest {
public static void main(String[] args) {
Predicate<Integer> predicate = new Predicate<Integer>() {
@Override
public boolean test(Integer integer) {
if (integer > 5) {
return true;
} else {
return false;
}
}
};
System.out.println(predicate.test(6));
System.out.println("********************");
predicate = integer -> integer > 5;
System.out.println(predicate.test(5));
}
}
2.4 Supplier(供应商)
Supplier接口是一个供给型的接口,其实就是一个容器,可以用来存储数据,然后可以供其他方法使用的这么一个接口。
@FunctionalInterface
public interface Supplier<T> {
//获取结果值
T get();
}
- get方法:获取返回值。
public class SupplierTest {
public static void test2() {
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
Optional<Integer> first = stream.filter(integer -> integer > 6).findFirst();
//orElse,如果first中存在数,就返回这个数,如果不存在,就放回传入的数
System.out.println(first.orElse(777));
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));
}
public static void test() {
Supplier<Integer> supplier = new Supplier<Integer>() {
@Override
public Integer get() {
return new Random().nextInt();
}
};
System.out.println(supplier.get());
System.out.println("********************");
supplier = () -> new Random().nextInt();
System.out.println(supplier.get());
System.out.println("********************");
Supplier<Double> supplier1 = Math::random;
System.out.println(supplier1.get());
}
public static void main(String[] args) {
test2();
}
}
2.5 小结
- Function<T, R> accept:接受一个参数,返回一个结果值
- Consumer
accept:接受一个参数,无返回值 - Predicate
: 接受一个参数,返回一个布尔值 - Supplier
get:无参数,返回一个结果值
三、其他接口
3.1 BiFunction函数
@FunctionalInterface
public interface BiFunction<T, U, R> {
// 接受两个参数并返回一个结果
R apply(T t, U u);
// 返回一个组合函数,首先将当前函数应用于输入,然后将结果作为参数传递给after函数。
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
BiFunction类,双向接口类,提供了两个输入参数,一个输出参数
public class BiFunctionTest {
public static void main(String[] args) {
//使用BiFunction来完成
System.out.println(test2.compute3(1, 2, (value1, value2) -> value1 + value2));
System.out.println(test2.compute3(1, 2, (value1, value2) -> value1 - value2));
System.out.println(test2.compute3(1, 2, (value1, value2) -> value1 * value2));
System.out.println(test2.compute3(1, 2, (value1, value2) -> value1 / value2));
//使用BiFunction中的andThen.
System.out.println(test2.compute4(2, 3, (value1, value2) -> value1 + value2,
value -> value * value));
}
//BiFunction类的使用。有两个输入参数
public int compute3(int a, int b, BiFunction<Integer, Integer, Integer> biFunction) {
return biFunction.apply(a, b);
}
public int compute4(int a, int b, BiFunction<Integer, Integer, Integer> biFunction,
Function<Integer, Integer> function) {
return biFunction.andThen(function).apply(a, b);
}
}
为什么BiFunction类中没有Compose方法呢?
倒推一下:因为如果有Compose方法,会先执行参数的Function。无论参数是Function还是BiFunction,返回值也都是一个值,然后就没办法再去执行BiFunction。
3.2 Operator(操作员)
除了Function,Consumer,Predicate,Supplier这几个基本的函数形式,还有其它派生的函数形式,它们扩展了基本的函数形式,包括UnaryOperator(extends Function)和BinaryOperator(extends BiFunction)。
UnaryOperator是一元操作符的意思,接收一个泛型T对象参数,返回相同T类型对象。BinaryOperator是二元操作符的意思,接收两个相同泛型参数类型T,返回R类型对象。
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
与
Function接口一样,UnaryOperator会传递一个值并返回一个值。但是,UnaryOperator和Function之间的区别在于,UnaryOperator必须返回与其传递的对象类型完全相同的对象。
例如,如果将“Person”对象传递给UnaryOperator,则必须将“Person”对象传递回。
public class OperatorTest {
public static void main(String[] args) {
UnaryOperator<String> unaryOperator = greet -> greet + " Bob!";
System.out.println(unaryOperator.apply("Hello")); // Hello Bob!
UnaryOperator<String> unaryOperator = greet -> greet + " Bob!";
UnaryOperator<String> unaryOperator1 = greet -> greet + " Jack!";
String greet = unaryOperator.andThen(unaryOperator1).apply("Hello");
System.out.println(greet); // Hello Bob! Jack!
UnaryOperator<String> unaryOperator = greet -> greet + " Bob!";
UnaryOperator<String> unaryOperator1 = greet -> greet + " Jack!";
String greet = unaryOperator.compose(unaryOperator1).apply("Hello");
System.out.println(greet); // Hello Jack! Bob!
}
}
四、底层实现
@FunctionalInterface注解的实现非常简单,它仅仅是一个标记注解,没有任何属性。然而,编译器在处理标有该注解的接口时,会执行额外的检查,确保接口符合函数式接口的定义。
4.1 编译器检查
在编译时,Java编译器会检查被标记为@FunctionalInterface的接口是否满足以下条件:
- 必须有且只有一个抽象方法。
- 可以有默认方法或静态方法,但这些方法不会被计入抽象方法的数量中。
- 继承的抽象方法不能超过一个。如果接口继承了多个接口,这些接口各自的抽象方法必须能够结合为一个方法。例如,如果一个接口继承了两个接口,这两个接口各自定义了相同签名的抽象方法,那么它们被视为一个方法。
4.2 与Object类方法的关系
函数式接口的抽象方法不能是Object类中的方法,因为Object中的方法在所有类中默认都有实现。如果在函数式接口中只声明Object类的方法(例如toString()),那么这个接口并不会被认为是一个有效的函数式接口。
4.3 lambda表达式使用
定义一个简单的Person类,然后使用lambda表达式解决一些问题
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
public class PersonTest {
public static void main(String[] args) {
Person person1 = new Person("zhangsan", 20);
Person person2 = new Person("lisi", 30);
Person person3 = new Person("wangwu", 40);
List<Person> list = Arrays.asList(person1, person2, person3);
PersonTest test = new PersonTest();
//测试第一个方法。
List<Person> list1 = test.getPersonByName("zhangsan", list);
list1.forEach(person -> System.out.println(person.getName()));
//测试第二种方法
List<Person> personByAge = test.getPersonByAge(20, list);
personByAge.forEach(person -> System.out.println(person.getAge()));
//测试第三方法
List<Person> peopleList = test.getPersonByArg(20, list,
(age, personList) -> personList.stream()
.filter(person -> person.getAge() > age).collect(Collectors.toList()));
peopleList.forEach(person -> System.out.println(person.getName()));
}
//使用lambda表达式定义一个 处理的方法
//filter 方法,参数是一个Predicate 谓语
//stream 提供了一个将流转换成集合的方法 collect(Collectors.toList())
public List<Person> getPersonByUsername(String name, List<Person> personList) {
return personList.stream()
.filter(person -> person.getName().equals("zhangsan"))
.collect(Collectors.toList());
}
//第二种方式,先直接使用lambda表达式将BiFunction定义好,然后直接将方法的两个参数传入到BiFunction.
public List<Person> getPersonByAge(int age, List<Person> personList) {
BiFunction<Integer, List<Person>, List<Person>> biFunction = (ageArg, Persons) -> {
return Persons.stream().filter(person -> person.getAge() > ageArg).collect(Collectors.toList());
};
return biFunction.apply(age, personList);
}
//第三种方式,动作也让他自己传递。 函数式接口的好处。
public List<Person> getPersonByArg(int age, List<Person> personList, BiFunction<Integer, List<Person>,
List<Person>> biFunction) {
return biFunction.apply(age, personList);
}
}
@Data
@AllArgsConstructor
@NoArgsConstructor
class Person {
private String name;
private Integer age;
}
真谛:函数式接口,传递的是行为,而不是数据。
五、实际应用
在实际开发中,函数式接口广泛应用于Java的标准库中,尤其是在java.util.function包中,提供了一组通用的函数式接口,如Predicate<T>、Function<T, R>、Supplier<T>等。
5.1 资源清理(自动关闭)
场景:需要在操作完成后自动释放资源(如文件、数据库连接)。
@FunctionalInterface
public interface ResourceHandler<T> {
void handle(T resource) throws IOException;
// 封装资源打开和关闭逻辑
static <T extends AutoCloseable> void useResource(
Supplier<T> resourceSupplier,
ResourceHandler<T> handler) throws IOException {
try (T resource = resourceSupplier.get()) {
handler.handle(resource);
}
}
}
public class ResourceTest {
public static void main(String[] args) throws IOException {
// 使用示例:自动关闭文件流
ResourceHandler.useResource(
() -> new FileInputStream("data.txt"), // 资源提供者
inputStream -> {
byte[] data = inputStream.readAllBytes();
System.out.println("读取到 " + data.length + " 字节");
} // 处理逻辑(无需手动关闭)
);
}
}
优势:将资源获取/释放与业务逻辑解耦,避免忘记关闭资源。
5.2 策略模式(动态算法选择)
场景:根据运行时条件选择不同的处理策略。
@FunctionalInterface
public interface PaymentStrategy {
void pay(BigDecimal amount);
}
public class PaymentService {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(BigDecimal amount) {
strategy.pay(amount);
}
}
public class PaymentTest {
public static void main(String[] args) {
// 使用示例:动态切换支付方式
PaymentService service = new PaymentService();
// 选择支付宝支付
service.setStrategy(amount ->
System.out.println("支付宝支付:" + amount + "元"));
service.executePayment(new BigDecimal("100.00"));
// 切换为微信支付
service.setStrategy(amount ->
System.out.println("微信支付:" + amount + "元"));
service.executePayment(new BigDecimal("200.00"));
}
}
优势:避免继承导致的类膨胀,策略实现更灵活。
5.3 流水线处理(链式操作)
场景:对数据依次进行多个处理步骤(如数据清洗、转换)。
@FunctionalInterface
public interface DataProcessor<T> {
T process(T data);
// 组合多个处理器
default DataProcessor<T> andThen(DataProcessor<T> next) {
return data -> next.process(this.process(data));
}
}
public class DataPipelineTest {
public static void main(String[] args) {
// 定义处理步骤
DataProcessor<String> trimProcessor = String::trim;
DataProcessor<String> upperCaseProcessor = String::toUpperCase;
DataProcessor<String> appendProcessor = s -> s + "!";
// 组合流水线
DataProcessor<String> pipeline = trimProcessor
.andThen(upperCaseProcessor)
.andThen(appendProcessor);
String result = pipeline.process(" hello "); // 输出 "HELLO!"
}
}
优势:通过链式调用实现可复用的处理流程。
5.4 异步任务编排
场景:多个异步任务需要顺序执行或合并结果
@FunctionalInterface
public interface AsyncTask<T> {
CompletableFuture<T> execute();
// 组合任务:先执行当前任务,再执行后续任务
default <U> AsyncTask<U> thenCompose(Function<T, AsyncTask<U>> next) {
return () -> this.execute().thenCompose(
result -> next.apply(result).execute()
);
}
}
public class AsyncTaskTest {
public static void main(String[] args) {
// 示例:异步获取用户信息 → 异步获取订单
AsyncTask<User> fetchUser = () -> userService.getUserAsync(1);
AsyncTask<Order> fetchOrder = user -> orderService.getOrderAsync(user.getId());
AsyncTask<Order> combinedTask = fetchUser.thenCompose(fetchOrder);
combinedTask.execute().thenAccept(order ->
System.out.println("订单详情:" + order)
);
}
}
5.5 条件触发(事件驱动)
场景:满足特定条件时触发操作(如监控阈值报警)。
@FunctionalInterface
public interface ConditionTrigger {
void triggerIf(Supplier<Boolean> condition, Runnable action);
}
// 实现类:定期检查条件
public class SchedulerTrigger implements ConditionTrigger {
@Override
public void triggerIf(Supplier<Boolean> condition, Runnable action) {
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(() -> {
if (condition.get()) {
action.run();
scheduler.shutdown();
}
}, 0, 1, TimeUnit.SECONDS);
}
}
public class ConditionTest {
public static void main(String[] args) {
// 使用示例:CPU使用率超过80%时报警
SchedulerTrigger trigger = new SchedulerTrigger();
trigger.triggerIf(
() -> getCpuUsage() > 80.0, // 条件检测
() -> sendAlert("CPU过载!") // 触发动作
);
}
}
优势:将条件检测与响应动作解耦,便于复用。
六、总结
@FunctionalInterface是Java函数式编程的重要组成部分,它通过严格的编译检查和清晰的意图表达,使得函数式接口的使用更加安全和规范。理解其设计理念和底层实现,可以帮助开发者更好地利用lambda表达式和方法引用,从而编写更加简洁、灵活的代码。
七、拓展
7.1 默认方法与静态方法的混淆
很多开发者会错误地认为函数式接口不能有多个方法,这实际上是错误的理解。函数式接口可以有多个默认方法和静态方法,但这些方法不会影响其函数式接口的本质。
7.2 隐式声明
尽管@FunctionalInterface注解可以显式地声明意图,但它并不是必须的。如果没有加这个注解,只要接口符合函数式接口的定义(只有一个抽象方法),它依然是一个有效的函数式接口。

浙公网安备 33010602011771号