java8 新特性之函数式接口
参考文章:菜鸟教程 https://www.runoob.com/java/java8-functional-interfaces.html
函数式接口介绍(摘自菜鸟):
函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
函数式接口可以被隐式转换为 lambda 表达式。
Lambda 表达式和方法引用(实际上也可认为是Lambda表达式)上。
如定义了一个函数式接口如下:
@FunctionalInterface interface GreetingService { void sayMessage(String message); }
那么就可以使用Lambda表达式来表示该接口的一个实现(注:JAVA 8 之前一般是用匿名类实现的):
GreetingService greetService1 = message -> System.out.println("Hello " + message);
java.util.function 它包含了很多类,用来支持 Java的 函数式编程,该包中的函数式接口有(对于英文不好的我,这个表格也算是照抄下来了):
序号 | 接口 & 描述 |
---|---|
1 | BiConsumer<T,U>
代表了一个接受两个输入参数的操作,并且不返回任何结果 |
2 | BiFunction<T,U,R>
代表了一个接受两个输入参数的方法,并且返回一个结果 |
3 | BinaryOperator<T>
代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果 |
4 | BiPredicate<T,U>
代表了一个两个参数的boolean值方法 |
5 | BooleanSupplier
代表了boolean值结果的提供方 |
6 | Consumer<T>
代表了接受一个输入参数并且无返回的操作 |
7 | DoubleBinaryOperator
代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。 |
8 | DoubleConsumer
代表一个接受double值参数的操作,并且不返回结果。 |
9 | DoubleFunction<R>
代表接受一个double值参数的方法,并且返回结果 |
10 | DoublePredicate
代表一个拥有double值参数的boolean值方法 |
11 | DoubleSupplier
代表一个double值结构的提供方 |
12 | DoubleToIntFunction
接受一个double类型输入,返回一个int类型结果。 |
13 | DoubleToLongFunction
接受一个double类型输入,返回一个long类型结果 |
14 | DoubleUnaryOperator
接受一个参数同为类型double,返回值类型也为double 。 |
15 | Function<T,R>
接受一个输入参数,返回一个结果。 |
16 | IntBinaryOperator
接受两个参数同为类型int,返回值类型也为int 。 |
17 | IntConsumer
接受一个int类型的输入参数,无返回值 。 |
18 | IntFunction<R>
接受一个int类型输入参数,返回一个结果 。 |
19 | IntPredicate
:接受一个int输入参数,返回一个布尔值的结果。 |
20 | IntSupplier
无参数,返回一个int类型结果。 |
21 | IntToDoubleFunction
接受一个int类型输入,返回一个double类型结果 。 |
22 | IntToLongFunction
接受一个int类型输入,返回一个long类型结果。 |
23 | IntUnaryOperator
接受一个参数同为类型int,返回值类型也为int 。 |
24 | LongBinaryOperator
接受两个参数同为类型long,返回值类型也为long。 |
25 | LongConsumer
接受一个long类型的输入参数,无返回值。 |
26 | LongFunction<R>
接受一个long类型输入参数,返回一个结果。 |
27 | LongPredicate
R接受一个long输入参数,返回一个布尔值类型结果。 |
28 | LongSupplier
无参数,返回一个结果long类型的值。 |
29 | LongToDoubleFunction
接受一个long类型输入,返回一个double类型结果。 |
30 | LongToIntFunction
接受一个long类型输入,返回一个int类型结果。 |
31 | LongUnaryOperator
接受一个参数同为类型long,返回值类型也为long。 |
32 | ObjDoubleConsumer<T>
接受一个object类型和一个double类型的输入参数,无返回值。 |
33 | ObjIntConsumer<T>
接受一个object类型和一个int类型的输入参数,无返回值。 |
34 | ObjLongConsumer<T>
接受一个object类型和一个long类型的输入参数,无返回值。 |
35 | Predicate<T>
接受一个输入参数,返回一个布尔值结果。 |
36 | Supplier<T>
无参数,返回一个结果。 |
37 | ToDoubleBiFunction<T,U>
接受两个输入参数,返回一个double类型结果 |
38 | ToDoubleFunction<T>
接受一个输入参数,返回一个double类型结果 |
39 | ToIntBiFunction<T,U>
接受两个输入参数,返回一个int类型结果。 |
40 | ToIntFunction<T>
接受一个输入参数,返回一个int类型结果。 |
41 | ToLongBiFunction<T,U>
接受两个输入参数,返回一个long类型结果。 |
42 | ToLongFunction<T>
接受一个输入参数,返回一个long类型结果。 |
43 | UnaryOperator<T>
接受一个参数为类型T,返回值类型也为T。 |
代码示例:
菜鸟教程的示例略
BiConsumer<T,U>
代表了一个接受两个输入参数的操作,并且不返回任何结果
// 想不到更好的示例了,就测试一下 BiConsumer 应该一次性单独逻辑,不依赖的 eval2(list, (k,v) -> {System.out.println("传入的参数1为:"+k + " 传入的参数2为:"+v);}); public static void eval2(List<Integer> list, BiConsumer<Integer,String> biConsumer) { for(Integer n: list) {biConsumer.accept(n,"pa2");} }
输出结果为:
传入的参数1为:1 传入的参数2为:pa2 传入的参数1为:2 传入的参数2为:pa2 传入的参数1为:3 传入的参数2为:pa2 传入的参数1为:4 传入的参数2为:pa2 传入的参数1为:5 传入的参数2为:pa2 传入的参数1为:6 传入的参数2为:pa2 传入的参数1为:7 传入的参数2为:pa2 传入的参数1为:8 传入的参数2为:pa2 传入的参数1为:9 传入的参数2为:pa2
BinaryOperator<T>
代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果
// 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果 // 逻辑处理,并返回与参数类型一样的结果 eval3(list, (k,v) -> v+k); public static void eval3(List<Integer> list, BinaryOperator<String> binaryOperator) { for(Integer n: list) {System.out.println(binaryOperator.apply(String.valueOf(n),"abc"));} }
这个方法泛型只有一个,却要传递两个参数。结果如下:
abc1
abc2
abc3
abc4
abc5
abc6
abc7
abc8
abc9
BiPredicate<T,U>
代表了一个两个参数的boolean值方法
// BiPredicate<T,U> 方法介绍:代表了一个两个参数的boolean值方法 // 处理逻辑并返回boolean类型,可穿两个不同类型参数。 eval4(list, (k,v) -> v.length()==k); public static void eval4(List<Integer> list, BiPredicate<Integer,String> biPredicate) { for(Integer n: list) {System.out.println(biPredicate.test(n,"213"));} }
这个可以传入两个不同的类型进行对比,返回boolean类型。结果如下:
false false true false false false false false false
BiFunction<T,U,R>
代表了一个接受两个输入参数的方法,并且返回一个结果
// BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果 // 三个类型都可自定义,相当于处理两个参数后返回结果。 eval5(list, (k,v) -> v.length()==k); public static void eval5(List<Integer> list, BiFunction<Integer,String,Boolean> biFunction) { for(Integer n: list) {System.out.println(biFunction.apply(n,"213"));} }
结果如下:
false false true false false false false false false
代码没有具体含义,只是学习一下,其实只要看几个大概就能理解这个函数式接口的大概逻辑了。
有一些自带的函数式接口,除了唯一的一个抽象方法,还有一些默认方法,还是很好的。可以看下源码,暂时无业务需求。
贴上完整的练习代码如下:

package cn.wh.java8.chapter.one; import java.util.Arrays; import java.util.List; import java.util.function.*; public class FunctionInterface { public static void main(String args[]){ List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9); // // 菜鸟示例:start // // Predicate<Integer> predicate = n -> true // // n 是一个参数传递到 Predicate 接口的 test 方法 // // n 如果存在则 test 方法返回 true // System.out.println("输出所有数据:"); // // // 传递参数 n // eval(list, n->true); // // // Predicate<Integer> predicate1 = n -> n%2 == 0 // // n 是一个参数传递到 Predicate 接口的 test 方法 // // 如果 n%2 为 0 test 方法返回 true // // System.out.println("输出所有偶数:"); // eval(list, n-> n%2 == 0 ); // // // Predicate<Integer> predicate2 = n -> n > 3 // // n 是一个参数传递到 Predicate 接口的 test 方法 // // 如果 n 大于 3 test 方法返回 true // // System.out.println("输出大于 3 的所有数字:"); // eval(list, n-> n > 3 ); // // 菜鸟示例: end; // 想不到更好的示例了,就测试一下 BiConsumer 应该一次性单独逻辑,不依赖的 // eval2(list, (k,v) -> {System.out.println("传入的参数1为:"+k + " 传入的参数2为:"+v);}); // 代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果 // 逻辑处理,并返回与参数类型一样的结果 // eval3(list, (k,v) -> v+k); // BiPredicate<T,U> 方法介绍:代表了一个两个参数的boolean值方法 // 处理逻辑并返回boolean类型,可穿两个不同类型参数。 // eval4(list, (k,v) -> v.length()==k); // BiFunction<T,U,R> 代表了一个接受两个输入参数的方法,并且返回一个结果 // 三个类型都可自定义,相当于处理两个参数后返回结果。 eval5(list, (k,v) -> v.length()==k); } public static void eval(List<Integer> list, Predicate<Integer> predicate) { for(Integer n: list) {if(predicate.test(n)) {System.out.println(n + " "); }} } public static void eval2(List<Integer> list, BiConsumer<Integer,String> biConsumer) { for(Integer n: list) {biConsumer.accept(n,"pa2");} } public static void eval3(List<Integer> list, BinaryOperator<String> binaryOperator) { for(Integer n: list) {System.out.println(binaryOperator.apply(String.valueOf(n),"abc"));} } public static void eval4(List<Integer> list, BiPredicate<Integer,String> biPredicate) { for(Integer n: list) {System.out.println(biPredicate.test(n,"213"));} } public static void eval5(List<Integer> list, BiFunction<Integer,String,Boolean> biFunction) { for(Integer n: list) {System.out.println(biFunction.apply(n,"213"));} } }
写完函数式接口,有个疑问函数式接口和抽象类有啥区别?除了接口和函数的区别,还有那些区别。以下摘自知乎,还不是很能说服自己。
在 java 8 之前,接口与其实现类之间的 耦合度 太高了(tightly coupled),当需要为一个接口添加方法时,所有的实现类都必须随之修改。默认方法解决了这个问题,它可以为接口添加新的方法,而不会破坏已有的接口的实现。这在 lambda 表达式作为 java 8 语言的重要特性而出现之际,为升级旧接口且保持向后兼容(backward compatibility)提供了途径。
- 接口可以被类多实现(被其他接口多继承),抽象类只能被单继承。
- 接口中没有
this
,没有构造函数,不能拥有实例字段(实例变量)或实例方法,无法保存 状态(state),抽象方法中可以。 - 抽象类不能在 java 8 的 lambda 表达式中使用。
- 从设计理念上,接口反映的是 “like-a” 关系,抽象类反映的是 “is-a” 关系。
作者:魔王不造反
链接:https://www.zhihu.com/question/68474140/answer/263792395
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
摘自菜鸟教程的评论
函数式接口里是可以包含静态方法,因为静态方法不能是抽象方法,是一个已经实现了的方法,所以是符合函数式接口的定义的;
3、函数式接口里允许定义 java.lang.Object 里的 public 方法
函数式接口里是可以包含Object里的public方法,这些方法对于函数式接口来说,不被当成是抽象方法(虽然它们是抽象方法);因为任何一个函数式接口的实现,默认都继承了 Object 类,包含了来自 java.lang.Object 里对这些抽象方法的实现;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类