JDK 8 函数型接口
一、简介
如果一个接口中只有一个方法,那么该接口就称为函数型接口,对于函数型接口,我们会加上注解 @FunctionalInterface 进行校验,如果某一个接口中存在两个抽象方法,那么它就会报错.
例如 JDK 8 内置的四大函数型接口之一 Function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | @FunctionalInterface public interface Function<T, R> { R apply(T t); default <V> Function<V, R> compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } 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; } } |
二、函数型接口中的 default、static 修饰的方法
函数型接口的定义不是只能有一个抽象方法吗,为什么 JDK 8 内置的函数型接口 Function 有 apply、compose、andThen、identity 四个方法呢,这不是和定义相违背了吗?
JDK 8 之前接口的结构如下
1 2 3 4 | interface 接口名{ 静态常量; 抽象方法; } |
JDK 8 及之后接口的结构如下
1 2 3 4 5 6 | interface 接口名{ 静态常量; 抽象方法; 默认方法; 静态方法; } |
那么默认方法、静态方法有什么用呢,下面我们就来探究一下
2.1、默认方法
假设有这么一个场景,我们需要在 Map 接口中增加一个抽象方法,扩展 Map 集合的功能
一般的做法是在顶层接口中增加新的抽象方法,然后实现接口的子类重写该新增的方法即可,但是 Map 集合的实现类众多,每个实现类都去重写这个新增的方法,那么这个工作量是比较大的
如何解决这个问题呢,这就需要使用 JDK 8 接口新特性默认方法
接口中的默认方法使用 default 关键字修饰
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // 自定义接口 interface A { void flying(String userName); // 注意接口中的默认方法和接口中的抽象方法不同,接口中的默认方法是有方法体的 default void swimming(){ System.out.println( "接口 A 中的 swimming 方法" ); } } class B implements A{ @Override public void flying(String userName) { System.out.println( "实现类 B 中的 flying 方法" ); } } public class LambdaDemo { public static void main(String[] args) { A a = new B(); a.swimming(); } } |
输出结果
可以看出,实现类 B 并没有重写接口 A 中的 swimming 方法,但是却可以调用 swimming 方法,说明接口中的默认方法是可以被继承的
既然可以被继承,那么可以被重写吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | // 自定义接口 interface A { void flying(String userName); // 注意接口中的默认方法和接口中的抽象方法不同,接口中的默认方法是有方法体的 default void swimming(){ System.out.println( "接口 A 中的 swimming 方法" ); } } class B implements A{ @Override public void flying(String userName) { System.out.println( "实现类 B 中的 flying 方法" ); } @Override public void swimming() { System.out.println( "实现类 B 中的 swimming 方法" ); } } public class LambdaDemo { public static void main(String[] args) { A a = new B(); a.swimming(); } } |
输出结果
接口中的默认方法也是可以被重写的
2.2、静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // 自定义接口 interface A { void flying(String userName); // 注意接口中的默认方法和接口中的抽象方法不同,接口中的默认方法是有方法体的 static void jumping(){ System.out.println( "接口 A 中的 swimming 方法" ); } } class B implements A{ @Override public void flying(String userName) { System.out.println( "实现类 B 中的 flying 方法" ); } } public class LambdaDemo { public static void main(String[] args) { // 接口中的静态方法不能被继承,由于是静态方法也不存在重写一说,只能通过 类名.方法名() 的方式进行调用 A.jumping(); } } |
输出结果
2.3、总结
上面我们想往 Map 接口中增加一个新方法,但是又不想去每个实现类里面重写该方法,这种场景下就可以将该方法定义为默认(default)方法,即子类中
1、接口中的默认方法(default)、静态方法(static)与接口中的抽象方法不同,它们是有方法体的
2、接口中的默认方法可以被子类继承、重写
3、接口中的静态方法只能使用类名进行调用
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?