问题

在编写一个API时, 须要查问多张表, 但又须要分页. 所以从哪些库中取, 以及取多少, 须要写判断语句和计算失去. 但编写时发现, 大部分的分页逻辑查问, 不同的仅仅调用的dao层函数不同. 所以想将须要调用的dao层函数, 作为参数传入到封装好的分页函数里.

但惋惜的是, Java里并没有函数指针. 不过能够用反射或函数式编程解决该问题.

什么是函数式编程

通过合并现有代码来生成新性能而不是从头开始编写所有内容,咱们能够更快地取得更牢靠的代码。代码个别状况下是操控数据, 数据并合成, 组合, 转换. 而函数式编程是 操作代码片段 . 益处是能缩小代码量, 而且能形象代码片段中雷同的逻辑, 从而更好保护和批改代码.

函数编程特点之一: 容许把函数自身作为参数传入另一个函数,还容许返回一个函数.

将函数作为参数传递的三种办法

基本原理: 通过多态实现.
参数为一个接口类, 通过接口类的不同实现, 从而实现对传递不同的函数.

调用一个接口里的函数, 该函数的逻辑会依据具体实现类变动而变动. 但不变的是参数, 返回值以及函数名.
// 函数式接口, 只有一个办法, 用于传递函数interface Strategy {    String approach(String msg);}// 一个实现Strategy接口的类, 目标是实现该接口的办法class Soft implements Strategy {    public String approach(String msg) {        return msg.toLowerCase() + "?";    }}// 一个与Strategy无关的类class Unrelated {    static String twice(String msg) {        return msg + " " + msg;    }}public class Strategize {    Strategy strategy;    String msg;    Strategize(String msg) {        strategy = new Soft(); // [1]        this.msg = msg;    }    void communicate() {        System.out.println(strategy.approach(msg));    }    void changeStrategy(Strategy strategy) {        this.strategy = strategy;    }    public static void main(String[] args) {        // 一个接口        Strategy[] strategies = {                //  Strategy 是一个接口, 应用匿名外部类实现该接口                new Strategy() {                    public String approach(String msg) {                        return msg.toUpperCase() + "!";                    }                },                // 匿名函数 Lambda 表达式                msg -> msg.substring(0, 5)                // 办法援用 Java8后反对,能够发现 approach 办法与 twice 办法都是承受一个String参数, 但 Unrelated 与 Strategy 接口没有任何关系, 如继承, 利用接口等                Unrelated::twice         };        Strategize s = new Strategize("Hello there");        s.communicate();        for(Strategy newStrategy : strategies) {            // 通过接口的不同实现类, 实现将函数作为参数传入, 从而失去不同的后果            s.changeStrategy(newStrategy);             s.communicate();         }    }}

办法援用

Java8 后反对. 根本形成为

[类型|对象名] :: 办法名称

// 函数式接口interface Callable {    void call(String s);}class Father {    private String name;      // 静态方法    static void find(String msg) {        System.out.println("I find a " + msg);    }    void setName(String name) {        this.name = name;        System.out.println(this.name);    }}public class Main {    public static void main(String[] args) {          // 能够视为 将 Father 里的 find函数赋值给 Callable 接口        Callable call = Father::find;        call.call("son");        // 实例化一个类        Father father = new Father();        // 接口办法由实例的setName实现        Callable call_1 = father::setName;        call_1.call("Jack");    }}

甚至反对构造函数的映射

interface MakeNoArgs {  Dog make();}class Dog {  String name;  Dog() {name="Jack";}} public static void main(String[] args) {    MakeNoArgs mna = Dog::new; // MakeNoArgs 接口的 make 办法由 Dog 的实例化办法实现       Dog myDog = mna.make(); }

@FunctionalInterface 函数式接口

函数式接口: 该接口有且仅有一个形象办法

函数式接口仍是一个接口, 只不过在接口的根底上, 增加一个条件而已, 即有且仅有一个形象办法. 除此之外能够将其当为一般接口应用

标注于函数式接口, 告知编译器查看实现该接口的办法只有一个

==该正文是可选的==

接口只蕴含一个形象办法,可称该接口为函数式接口

下面的接口都应该增加该正文, 告知编译器在编译器查看这些接口是否只有一个形象办法
// 编译失败, 函数式接口只应有一个形象办法, 而该接口有两个形象办法!@FunctionalInterfaceinterface NotFunctional {  // 两个形象办法, 不容许  String goodbye(String arg);  String hello(String arg);}

留神

  • 有默认实现的办法不算做一个形象办法
  • 如果一个接口申明了一个笼罩java.lang.Object的一个公共办法的形象办法,不计入该接口的形象办法数

    因为该接口的任何实现都有一个来自java.lang.Object或其余中央的实现
@FunctionalInterfaceinterface NotFunctional {    String goodbye(String arg);      // 不算做一个形象办法, NotFunctional类仍算作只有一个形象办法    default String hello(String arg) {        System.out.print(arg);        return arg;    }}

在 Java 中有哪些是函数式接口呢?
最常见的有

  • Comparator 接口
  • Runnable 接口

例子

要求输出两个整数, 与一个函数 (该函数承受两个整数, 返回一个整数).

两个整数在执行该函数时, 两个整数必须各自 - 100 后执行传入的函数, 并将其后果返回

@FunctionalInterfaceinterface Transform {    int transform(int num1, int num2);}public class Main {    public static int doTransform(int num1, int num2, Transform tra) {        num1 -= 100;        num2 -= 100;        return tra.transform(num1, num2);    }    public static int add(int num1, int num2) {        return num1 + num2;    }    public static int multiple(int num1, int num2) {        return num1 * num2;    }    public static void main(String[] args) {        int num1 = 200;        int num2 = 200;        System.out.println(doTransform(num1, num2, Main::add));        System.out.println(doTransform(num1, num2, Main::multiple));    }}// 或者用Java内置的函数式接口public class Main {    public static int doTransform(int num1, int num2,BiFunction<Integer, Integer, Integer> biFunction) {        num1 -= 100;        num2 -= 100;        return  biFunction.apply(num1, num2);    }    public static int add(int num1, int num2) {        return num1 + num2;    }    public static int multiple(int num1, int num2) {        return num1 * num2;    }    public static void main(String[] args) {        int num1 = 200;        int num2 = 200;        System.out.println(doTransform(num1, num2, Main::add));        System.out.println(doTransform(num1, num2, Main::multiple));    }}

想法

让我想到 Filter 拦截器的作用. 对一个 request 进行解决, 尽管两头逻辑的不同, 但预处理和查看都是雷同. 若没有拦截器. 只有两头逻辑不同, 预处理和查看的代码就要反复一次. 而拦截器的存在, 让咱们雷同的逻辑只用写一次. 代码量更少了, 也更好保护和批改. 而上述代码中也是同理, - 100 相当于预处理, 而 add, multiple 相当于两头的不同逻辑. 应用函数式接口, 能够帮忙对立逻辑, 实现代码重用.

参考

  • On Java8
  • 廖雪峰Java