JDK 8 函数型接口

一、简介

如果一个接口中只有一个方法,那么该接口就称为函数型接口,对于函数型接口,我们会加上注解 @FunctionalInterface 进行校验,如果某一个接口中存在两个抽象方法,那么它就会报错.

例如 JDK 8 内置的四大函数型接口之一 Function

@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 之前接口的结构如下

interface 接口名{
  静态常量;
  抽象方法;  
}

JDK 8 及之后接口的结构如下

interface 接口名{
  静态常量;
  抽象方法;
  默认方法;
  静态方法;
}

那么默认方法、静态方法有什么用呢,下面我们就来探究一下

2.1、默认方法

假设有这么一个场景,我们需要在 Map 接口中增加一个抽象方法,扩展 Map 集合的功能

一般的做法是在顶层接口中增加新的抽象方法,然后实现接口的子类重写该新增的方法即可,但是 Map 集合的实现类众多,每个实现类都去重写这个新增的方法,那么这个工作量是比较大的

如何解决这个问题呢,这就需要使用 JDK 8 接口新特性默认方法

接口中的默认方法使用 default 关键字修饰

// 自定义接口
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 方法,说明接口中的默认方法是可以被继承的

既然可以被继承,那么可以被重写吗?

// 自定义接口
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、静态方法

// 自定义接口
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 @ 2022-07-08 18:25  变体精灵  阅读(128)  评论(0)    收藏  举报