关于java:Java函数式编程Java里的函数指针

52次阅读

共计 3986 个字符,预计需要花费 10 分钟才能阅读完成。

问题

在编写一个 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 函数式接口

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

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

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

== 该正文是可选的 ==

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

下面的接口都应该增加该正文, 告知编译器在编译器查看这些接口是否只有一个形象办法

// 编译失败, 函数式接口只应有一个形象办法, 而该接口有两个形象办法!
@FunctionalInterface
interface NotFunctional {
  // 两个形象办法, 不容许
  String goodbye(String arg);
  String hello(String arg);
}

留神

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

    因为该接口的任何实现都有一个来自 java.lang.Object 或其余中央的实现

@FunctionalInterface
interface NotFunctional {String goodbye(String arg);
      // 不算做一个形象办法, NotFunctional 类仍算作只有一个形象办法
    default String hello(String arg) {System.out.print(arg);
        return arg;
    }
}

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

  • Comparator 接口
  • Runnable 接口

例子

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

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

@FunctionalInterface
interface 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

正文完
 0