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、接口中的静态方法只能使用类名进行调用

 

posted @   变体精灵  阅读(116)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示