转载

JAVA基础(五)函数式接口-复用,解耦之利刃

JAVA基础(五)函数式接口-复用,解耦之利刃
Java极客  |  作者  /  铿然一叶
这是 Java极客 的第 67 篇原创文章

相关阅读:

JAVA基础(一)简单、透彻理解内部类和静态内部类

JAVA基础(二)内存优化-使用Java引用做缓存

JAVA基础(三)ClassLoader实现热加载

JAVA基础(四)枚举(enum)和常量定义,工厂类使用对比

JAVA编程思想(一)通过依赖注入增加扩展性

JAVA编程思想(二)如何面向接口编程

JAVA编程思想(三)去掉别扭的if,自注册策略模式优雅满足开闭原则

JAVA编程思想(四)Builder模式经典范式以及和工厂模式如何选?

HikariPool源码(二)设计思想借鉴

人在职场(一)IT大厂生存法则

1. 函数式接口

接口中只有一个抽象方法的接口称为函数式接口。函数式接口可以通过@FunctionalInterface注解来修饰,也可以不使用该注解,只要接口只有一个抽象方法则可。

2. 函数式接口使用例子

2.1. 服务和消费者

import java.util.function.Consumer;

public class Service {
    // 方法入参为函数式接口,函数定义为消费Event
    public void exec(Consumer<Event> consumer) {
        Event event = new Event(1001, "get data from cache.");
        consumer.accept(event);
    }
}

// event通知消费类,消费event的方式是将event发出去
public class EventNotify {
    // 方法名并不重要,不需要和函数式接口方法名一致,只要参数和返回值一致则可
    public void send(Event event) {
        System.out.println("send event: " + event.toString());
    }
}

// event记录消费类,消费event的方式是将event记录下来
public class EventLogger {
    // 方法名并不重要,不需要和函数式接口方法名一致,只要参数和返回值一致则可
    public static void log(Event event) {
        System.out.println("log event: " + event.toString());
    }
}

public class Event {
    private int eventId;
    private String content;

    public Event(int eventId, String content) {
        this.eventId = eventId;
        this.content = content;
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("eventId=").append(eventId).append(", ");
        builder.append("content=").append(content);
        return builder.toString();
    }

}
复制代码

2.2. 使用函数式接口

public class FunctionDemo {
    public static void main(String[] args) {
        Service service = new Service();
        EventNotify eventNotify = new EventNotify();

        // 使用类实例::方法名传入函数,这里必须使用类实例,是因为这个方法声明为非静态方法
        service.exec(eventNotify::send);

        // 使用类::方法名传入函数,这里可以使用类而非类实例,是因为这个方法声明为静态方法
        service.exec(EventLogger::log);
    }
}
复制代码

输出:

send event: eventId=1001, content=get data from cache.
log event: eventId=1001, content=get data from cache.
复制代码

看到这里,很多人会想,从这个例子看,函数式接口并没有什么优势,不用函数式接口,使用接口实现类也能实现啊.

2.3. 不用函数式接口实现例子

2.3.1. 消费者直接实现接口方法

class NonFunctionEventNotify<Event> implements Consumer<Event> {
    @Override
    public void accept(Event event) {
        System.out.println("send event: " + event.toString());
    }
}

// 事件记录类,消费event的方式是将event记录下来
class NonFunctionEventLogger<Event> implements Consumer<Event> {
    @Override
    public void accept(Event event) {
        System.out.println("log event: " + event.toString());
    }
}
复制代码

2.3.2. 使用接口实现类

public class FunctionDemo {
    public static void main(String[] args) {
        Service service = new Service();
        NonFunctionEventNotify nonFunctionEventNotify = new NonFunctionEventNotify();
        // 传入实现类实例
        service.exec(nonFunctionEventNotify);

        NonFunctionEventLogger nonFunctionEventLogger = new NonFunctionEventLogger();
        // 传入实现类实例
        service.exec(nonFunctionEventLogger);
    }
}
复制代码

可见,不用函数式接口也能实现对应功能,那为啥还要使用函数式接口呢?

2.3.2. 函数式接口和接口实现方式差异以及优点

差异如下:

实现方式 掺入参数 实现接口 方法名
函数式接口 可直接传入静态方法,省去类实例化动作 不需要实现任何接口 没有要求,只要方法入参和出参一样则可
接口实现类 必须有类实例,传入类实例的方法 需要实现指定接口 必须同接口方法完全一样

通过对比,函数式接口的优势就大大体现出来了。

  1. 方法入参到方法级,粒度比类小,这样自由度更高,更灵活,就好比拼乐高,零件粒度越小,越能充分发挥想象力,进行自由组合。
  2. 不要求必须实现某个接口,对方法名也没有要求,只要满足函数接口的入参和出参定义就行,这样已有的代码可能不用修改就能被利用,特别是当你手里没有源码可改时这点显得尤为重要(虽然可以写个包装类去做适配,但能不写不是更好么)

3. 总结

  1. 函数式接口可以做到方法级复用,自由度更高,更灵活
  2. 函数式接口的方法定义和调用者实现方法定义解耦,不需要方法一致,也不要求实现该接口。
  3. 方法定义解耦后,只要方法的入参和出参一致时,就可以复用已有代码,避免修改,复用性大大提高。

注:Java提供的函数式接口已经能满足大部分使用场景,这些接口可以在java.util.function包下找到。

end.

原文  https://juejin.im/post/5eaf98e26fb9a0437b76db91
正文到此结束
Loading...