函数式编程

  • 使用某种方式操纵其他代码
  • 不用从头开始编写大量代码,而是从易于理解、充分测试及可靠的现有小块开始,最后将它们组合在一起以创建新代码。

函数式编程

  • 通过合并现有代码来生成新功能而不是从头开始编写所有内容,我们可以更快地获得更可靠的代码。
  • OO(object oriented,面向对象)是抽象数据,FP(functional programming,函数式编程)是抽象行为。

如果希望方法在调用时行为不同,该怎么做呢?
只要能将代码传递给方法,我们就可以控制它的行为。

函数式接口:

  • 只有一个方法的接口 ```java interface Strategy { String approach(String msg); }
  1. 以其为模板
  2. ```java
  3. static String app(String s) {
  4. return s + "?";
  5. }
  6. public static void main(String[] args) {
  7. Strategy[] strategies = new Strategy[]{
  8. new Strategy() {
  9. @Override
  10. public String approach(String msg) {
  11. return "zms"; // 匿名内部类
  12. }
  13. },
  14. msg -> msg + "!", // lambda
  15. TriFunctionTest::app // 方法引用
  16. };

Lambda表达式

是使用最小可能语法来编写函数定义

方法引用:

方法引用,它以::为特征。::的左边是类或对象的名称, ::的右边是方法的名称,但是没有参数列表。
方法引用组成:类名或对象名,后面跟 ::,然后跟方法名称。

  1. public class TriFunctionTest {
  2. interface Callable { // [1]
  3. void call(String s);
  4. }
  5. public void func(String s) {
  6. System.out.println("TriFunctionTest.func");
  7. }
  8. public static void funStatic(String s) {
  9. System.out.println("TriFunctionTest.funStatic");
  10. }
  11. public static void main(String[] args) {
  12. Callable callable1 = new TriFunctionTest()::func;
  13. Callable callable2 = TriFunctionTest::funStatic;
  14. callable1.call("zms");
  15. callable2.call("zms");
  16. /**
  17. * TriFunctionTest.func
  18. * TriFunctionTest.funStatic
  19. */
  20. }
  21. }
  1. interface和引用的方法的名字不重要,重要的是返回值和参数是不是匹配
  2. 静态方法引用不需要实例化类对象
  3. 引用非静态方法需要实例化对象

基本特征

  • 参数和函数体被箭头->分隔开: msg -> msg + “!”
  • 当只用一个参数,可以不需要括号 ()
  • 如果没有参数,则必须使用括号 () 表示空参数列表。
  • 对于多个参数,将参数列表放在括号 () 中。
  • 单行的Lambda不使用return
  • 多行的必须使用{}+return

Lambda的例子

  1. public class TriFunctionTest {
  2. interface ClassA {
  3. String A(String a);
  4. }
  5. interface ClassB {
  6. String B(String a, Double b);
  7. }
  8. public static void main(String[] args) {
  9. ClassA classA = str -> str + "!";
  10. System.out.println(classA.A("zms"));
  11. ClassB classB = (str, b) -> str + b + "?";
  12. System.out.println(classB.B("zms", 88.0));
  13. }
  14. }
  15. zms!
  16. zms88.0?

Lambda递归

递归方法必须是成员变量或静态变量,否则会出现编译时错误。
1. 静态变量的情况

  1. public class TriFunctionTest {
  2. private static Fab f;
  3. interface Fab {
  4. int fab(int n);
  5. }
  6. public static void main(String[] args) {
  7. f = n -> n == 0 ? 1 : n == 1 ? 1 : f.fab(n - 1) + f.fab(n - 2);
  8. for (int i = 0; i < 10; i++) {
  9. System.out.println(f.fab(i));
  10. }
  11. }
  12. }
  1. 成员变量

    1. public class TriFunctionTest {
    2. Fab f;
    3. interface Fab {
    4. int fab(int n);
    5. }
    6. public static void main(String[] args) {
    7. TriFunctionTest triFunctionTest = new TriFunctionTest();
    8. triFunctionTest.f = n ->
    9. n == 0 ? 1 :
    10. n == 1 ? 1 :
    11. triFunctionTest.f.fab(n - 1) + triFunctionTest.f.fab(n - 2);
    12. for (int i = 0; i < 10; i++) {
    13. System.out.println(triFunctionTest.f.fab(i));
    14. }
    15. }
    16. }

    Runnable接口

    接口的方法不带参数以及返回值
    public abstract void run();

    1. public static void main(String[] args) {
    2. Runnable runnable = () -> {
    3. System.out.println("TriFunctionTest.main");
    4. };
    5. runnable.run();
    6. }

    例子2:

    1. public static void main(String[] args) {
    2. new Thread(() -> System.out.println("TriFunctionTest.main")).start();
    3. new Thread(() -> {
    4. System.out.println("zms");
    5. System.out.println("vms");
    6. }).start();
    7. // 也可以使用方法引用作为作为参数传入Thread
    8. }

    未绑定的方法引用

    指没有关联对象的非静态方法.
    使用未绑定的引用时,我们必须先提供对象:

    1. public class TriFunctionTest {
    2. static class X {
    3. String f() {
    4. return "X::F";
    5. }
    6. }
    7. interface MakeString {
    8. String make();
    9. }
    10. interface TransformX {
    11. String transform(X x);
    12. }
    13. public static void main(String[] args) {
    14. /*
    15. MakeString makeString = X::f; // 不能直接赋值,因为f()不是静态方法
    16. */
    17. TransformX transformX = X::f;
    18. X x = new X();
    19. System.out.println(transformX.transform(x));
    20. }
    21. }

    引用f()时候需要使用this对象,因为f()不是静态的方法,必须有一个对象对他进行调用.
    拿到未绑定的方法引用,并且调用它的transform()方法,将一个X类的对象传递给它,最后使得 x.f() 以某种方式被调用。Java知道它必须拿第一个参数,该参数实际就是this 对象,然后对此调用方法。

  • 如果你的方法有更多个参数,就以第一个参数接受this的模式来处理。

    1. public class TriFunctionTest {
    2. static class X {
    3. String f(int a) {
    4. return "X::F" + a;
    5. }
    6. }
    7. interface MakeString {
    8. String make();
    9. }
    10. interface TransformX {
    11. String transform(X x, int a);// 自动将a传给了x对象
    12. }
    13. public static void main(String[] args) {
    14. /*
    15. MakeString makeString = X::f; // 不能直接赋值,因为f()不是静态方法
    16. */
    17. TransformX transformX = X::f;
    18. X x = new X();
    19. System.out.println(transformX.transform(x,1)); // X::F1
    20. }
    21. }

    构造函数引用

    构造函数只有一个相同名称::: new,根据不同的参数确定具体的构造函数! ```java class Dog { String name; int age = -1; // For “unknown” Dog() { name = “stray”; } Dog(String nm) { name = nm; } Dog(String nm, int yrs) { name = nm; age = yrs; } }

public class TriFunctionTest { interface MakeNoArgs { Dog make(); }

  1. interface Make1Arg {
  2. Dog make(String nm);
  3. }
  4. interface Make2Args {
  5. Dog make(String nm, int age);
  6. }
  7. public static void main(String[] args) {
  8. MakeNoArgs noArgs = Dog::new;
  9. Make1Arg make1Arg = Dog::new;
  10. Make2Args make2Args = Dog::new;
  11. System.out.println(noArgs.make());
  12. System.out.println(make1Arg.make("zms"));
  13. System.out.println(make2Args.make("zms", 11));

// fp.Dog@6d03e736 // fp.Dog@568db2f2 // fp.Dog@378bf509 } }

  1. <a name="ZAz7O"></a>
  2. ## 函数式接口
  3. - 方法引用和 Lambda 表达式都必须被赋值,同时赋值需要类型信息才能使编译器保证类型的正确性。
  4. - Lambda 表达式包含 类型推导 (编译器会自动推导出类型信息,避免了程序员显式地声明)。编译器必须能够以某种方式推导出 x 的类型。
  5. - 当 Lambda 表达式被赋值时,编译器必须确定 x 和 y 的确切类型以生成正确的代码。

(x, y) -> x + y

  1. - 使用 `@`**`FunctionalInterface` **注解强制执行此“函数式方法”模式:
  2. - 每个接口只包含**一个抽象方法**,称为 _函数式方法_
  3. ```java
  4. // functional/FunctionalAnnotation.java
  5. @FunctionalInterface
  6. interface Functional {
  7. String goodbye(String arg);
  8. }
  9. interface FunctionalNoAnn {
  10. String goodbye(String arg);
  11. }
  12. /*
  13. @FunctionalInterface
  14. interface NotFunctional {
  15. String goodbye(String arg);
  16. String hello(String arg);
  17. }
  18. 产生错误信息:
  19. 函数式方法必须只有一个抽象函数
  20. */
  21. public class FunctionalAnnotation {
  22. public String goodbye(String arg) {
  23. return "Goodbye, " + arg;
  24. }
  25. public static void main(String[] args) {
  26. FunctionalAnnotation fa =
  27. new FunctionalAnnotation();
  28. Functional f = fa::goodbye;
  29. FunctionalNoAnn fna = fa::goodbye;
  30. // Functional fac = fa; //错误!FunctionalAnnotation 并没有显式地去实现 Functional 接口。
  31. Functional fl = a -> "Goodbye, " + a;
  32. FunctionalNoAnn fnal = a -> "Goodbye, " + a;
  33. }
  34. }

自带的函数接口

命名规则

  1. 处理的是对象而不是基本类型
    1. Function
    2. Consumer
    3. Predicate
  2. 接受的参数是基本类型,由名称的第一部分进行标识
    1. LongConsumer
    2. DoubleFunction
    3. IntPredicate
    4. 返回基本类型的Supplier(无参数返回任意类型)除外
  3. 如果返回值是基本类型,用To表示
    1. ToLongFunction<T>
    2. IntToLongFunction
  4. 返回类型和参数类型相同Operator
    1. UnaryOPerator单个参数
    2. BinaryOperator两个参数
  5. 接受参数并返回boolean使用谓词Predicate
  6. 接受的两个参数类型不同,名称中有一个Bi

系统接口

特征 函数式方法名 示例
无参数;
无返回值
Runnable
(java.lang)
run()
Runnable
无参数;
返回类型任意
Supplier
get()
getAs类型()
Supplier<T>
BooleanSupplier
IntSupplier
LongSupplier
DoubleSupplier
无参数;
返回类型任意
Callable
(java.util.concurrent)
call()
Callable<V>
1 参数;
无返回值
Consumer
accept()
Consumer<T>
IntConsumer
LongConsumer
DoubleConsumer
2 参数 Consumer BiConsumer
accept()
BiConsumer<T,U>
2 参数 Consumer
第一个参数是 引用;
第二个参数是 基本类型
Obj类型Consumer
accept()
ObjIntConsumer<T>
ObjLongConsumer<T>
ObjDoubleConsumer<T>
1 参数;
返回类型不同
Function
apply()
To类型类型To类型
applyAs类型()
Function<T,R>
IntFunction<R>
LongFunction<R>
DoubleFunction<R>
ToIntFunction<T>
ToLongFunction<T>
ToDoubleFunction<T>
IntToLongFunction
IntToDoubleFunction
LongToIntFunction
LongToDoubleFunction
DoubleToIntFunction
DoubleToLongFunction
1 参数;
返回类型相同
UnaryOperator
apply()
UnaryOperator<T>
IntUnaryOperator
LongUnaryOperator
DoubleUnaryOperator
2 参数,类型相同;
返回类型相同
BinaryOperator
apply()
BinaryOperator<T>
IntBinaryOperator
LongBinaryOperator
DoubleBinaryOperator
2 参数,类型相同;
返回整型
Comparator
(java.util)
compare()
Comparator<T>
2 参数;
返回布尔型
Predicate
test()
Predicate<T>
BiPredicate<T,U>
IntPredicate
LongPredicate
DoublePredicate
参数基本类型;
返回基本类型
类型To类型Function
applyAs类型()
IntToLongFunction
IntToDoubleFunction
LongToIntFunction
LongToDoubleFunction
DoubleToIntFunction
DoubleToLongFunction
2 参数;
类型不同
Bi操作
(不同方法名)
BiFunction<T,U,R>
BiConsumer<T,U>
BiPredicate<T,U>
ToIntBiFunction<T,U>
ToLongBiFunction<T,U>
ToDoubleBiFunction<T>

例子:
所有不同的Function变体实例

  1. // functional/FunctionVariants.java
  2. import java.util.function.*;
  3. class Foo {}
  4. class Bar {
  5. Foo f;
  6. Bar(Foo f) { this.f = f; }
  7. }
  8. class IBaz {
  9. int i;
  10. IBaz(int i) {
  11. this.i = i;
  12. }
  13. }
  14. class LBaz {
  15. long l;
  16. LBaz(long l) {
  17. this.l = l;
  18. }
  19. }
  20. class DBaz {
  21. double d;
  22. DBaz(double d) {
  23. this.d = d;
  24. }
  25. }
  26. public class FunctionVariants {
  27. static Function<Foo,Bar> f1 = f -> new Bar(f);
  28. static IntFunction<IBaz> f2 = i -> new IBaz(i);
  29. static LongFunction<LBaz> f3 = l -> new LBaz(l);
  30. static DoubleFunction<DBaz> f4 = d -> new DBaz(d);
  31. static ToIntFunction<IBaz> f5 = ib -> ib.i;
  32. static ToLongFunction<LBaz> f6 = lb -> lb.l;
  33. static ToDoubleFunction<DBaz> f7 = db -> db.d;
  34. static IntToLongFunction f8 = i -> i;
  35. static IntToDoubleFunction f9 = i -> i;
  36. static LongToIntFunction f10 = l -> (int)l;
  37. static LongToDoubleFunction f11 = l -> l;
  38. static DoubleToIntFunction f12 = d -> (int)d;
  39. static DoubleToLongFunction f13 = d -> (long)d;
  40. public static void main(String[] args) {
  41. Bar b = f1.apply(new Foo());
  42. IBaz ib = f2.apply(11);
  43. LBaz lb = f3.apply(11);
  44. DBaz db = f4.apply(11);
  45. int i = f5.applyAsInt(ib);
  46. long l = f6.applyAsLong(lb);
  47. double d = f7.applyAsDouble(db);
  48. l = f8.applyAsLong(12);
  49. d = f9.applyAsDouble(12);
  50. i = f10.applyAsInt(12);
  51. d = f11.applyAsDouble(12);
  52. i = f12.applyAsInt(13.0);
  53. l = f13.applyAsLong(13.0);
  54. }
  55. }

这些 Lambda 表达式尝试生成适合函数签名的最简代码。 在某些情况下有必要进行强制类型转换,否则编译器会报截断错误。

使用函数接口时,名称无关紧要——只要参数类型和返回类型相同。 Java 会将你的方法映射到接口方法。 要调用方法,可以调用接口的函数式方法名,例Runnable的run(),而不是你的方法名。

  1. // functional/ClassFunctionals.java
  2. import java.util.*;
  3. import java.util.function.*;
  4. class AA {}
  5. class BB {}
  6. class CC {}
  7. public class ClassFunctionals {
  8. static AA f1() { return new AA(); }
  9. static int f2(AA aa1, AA aa2) { return 1; }
  10. static void f3(AA aa) {}
  11. static void f4(AA aa, BB bb) {}
  12. static CC f5(AA aa) { return new CC(); }
  13. static CC f6(AA aa, BB bb) { return new CC(); }
  14. static boolean f7(AA aa) { return true; }
  15. static boolean f8(AA aa, BB bb) { return true; }
  16. static AA f9(AA aa) { return new AA(); }
  17. static AA f10(AA aa1, AA aa2) { return new AA(); }
  18. public static void main(String[] args) {
  19. Supplier<AA> s = ClassFunctionals::f1;
  20. s.get();
  21. Comparator<AA> c = ClassFunctionals::f2;
  22. c.compare(new AA(), new AA());
  23. Consumer<AA> cons = ClassFunctionals::f3;
  24. cons.accept(new AA());
  25. BiConsumer<AA,BB> bicons = ClassFunctionals::f4;
  26. bicons.accept(new AA(), new BB());
  27. Function<AA,CC> f = ClassFunctionals::f5;
  28. CC cc = f.apply(new AA());
  29. BiFunction<AA,BB,CC> bif = ClassFunctionals::f6;
  30. cc = bif.apply(new AA(), new BB());
  31. Predicate<AA> p = ClassFunctionals::f7;
  32. boolean result = p.test(new AA());
  33. BiPredicate<AA,BB> bip = ClassFunctionals::f8;
  34. result = bip.test(new AA(), new BB());
  35. UnaryOperator<AA> uo = ClassFunctionals::f9;
  36. AA aa = uo.apply(new AA());
  37. BinaryOperator<AA> bo = ClassFunctionals::f10;
  38. aa = bo.apply(new AA(), new AA());
  39. }
  40. }

每个方法名称都是随意的(如 f1()f2()等)。正如你刚才看到的,一旦将方法引用赋值给函数接口,我们就可以调用与该接口关联的函数方法。 在此示例中为 get()compare()accept()apply()test()

自定义接口

接口是有限的。比如有 BiFunction,但也仅此而已。 如果需要三参数函数的接口怎么办? 其实这些接口非常简单,很容易查看 Java 库源代码并自行创建。

  1. @FunctionalInterface
  2. public interface TriFunction<T, U, V, R> {
  3. R apply(T t, U u, V v);
  4. }
  1. // functional/TriFunctionTest.java
  2. public class TriFunctionTest {
  3. static int f(int i, long l, double d) { return 99; }
  4. public static void main(String[] args) {
  5. TriFunction<Integer, Long, Double, Integer> tf =
  6. TriFunctionTest::f; // 使用引用进行赋值
  7. tf = (i, l, d) -> 12; // 使用lambda进行赋值
  8. }
  9. }

高阶函数

  • 消费或者产生函数的函数

产生函数

  1. public class ProduceFunction {
  2. interface FuncSS extends Function<String, String> {
  3. }
  4. static FuncSS produce() {
  5. //返回一个FuncSS,所以下面的s可以知道是String
  6. return s -> s.toLowerCase();
  7. }
  8. public static void main(String[] args) {
  9. FuncSS funcSS = produce();
  10. System.out.println(funcSS.apply("VMS"));
  11. }
  12. }
  • produce() 是高阶函数。
    • 使用继承,可以轻松地为专用接口创建别名
    • 使用 Lambda 表达式,可以轻松地在方法中创建和返回一个函数。

    • 消费函数

      要消费函数,需要在参数列表正确的描述函数类型
      ** ```java class One { }

class Two { }

public class ProduceFunction { static Two consume(Function oneTwoFunction) { return oneTwoFunction.apply(new One()); }

  1. public static void main(String[] args) {
  2. System.out.println(consume(one -> new Two()));
  3. }

}

  1. 基于消费函数生成新函数:
  2. ```java
  3. class I {
  4. @Override
  5. public String toString() {
  6. return "I";
  7. }
  8. }
  9. class O {
  10. @Override
  11. public String toString() {
  12. return "O";
  13. }
  14. }
  15. public class ProduceFunction {
  16. static Function<I, O> transform(Function<I, O> in) {
  17. return in.andThen(o -> {
  18. System.out.println(o);
  19. return o;
  20. });
  21. }
  22. public static void main(String[] args) {
  23. Function<I, O> f = transform(i -> {
  24. System.out.println(i);
  25. return new O();
  26. });
  27. O o = f.apply(new I());
  28. }
  29. }

使用到了 Function 接口中名为 andThen() 的默认方法,该方法专门用于操作函数。
在调用 in 函数之后调用 andThen().
要附加一个 andThen() 函数,我们只需将该函数作为参数传递。
transform() 产生的是一个新函数,它将 in 的动作与 andThen() 参数的动作结合起来。

闭包

如果一个lambda使用了作用域之外的变量,返回该函数会发生什么?


Lambda 可以没有限制地引用 实例变量和静态变量。但 局部变量必须显式声明为final,或事实上是final 。

引用外部变量

  1. // functional/Closure1.java
  2. import java.util.function.*;
  3. public class Closure1 {
  4. int i; // 对于函数来说是一个外部变量
  5. IntSupplier makeFun(int x) {
  6. return () -> x + i++;
  7. }
  8. }

如果你对同一个对象多次调用 makeFun() ,你最终会得到多个函数,它们共享 i 的存储空间:

  1. class Closure1 {
  2. int i;
  3. IntSupplier makeFun(int x) {
  4. return () -> x + i++;
  5. }
  6. }
  7. public class ProduceFunction {
  8. public static void main(String[] args) {
  9. Closure1 closure1 = new Closure1();
  10. IntSupplier f1 = closure1.makeFun(0);
  11. IntSupplier f2 = closure1.makeFun(0);
  12. IntSupplier f3 = closure1.makeFun(0);
  13. System.out.println(f1.getAsInt());
  14. System.out.println(f2.getAsInt());
  15. System.out.println(f3.getAsInt());
  16. /*
  17. 0
  18. 1
  19. 2
  20. */
  21. }
  22. }

每次调用 getAsInt() 都会增加 i,表明存储是共享的。

引用局部变量

  1. class Closure1 {
  2. IntSupplier makeFun(int x) {
  3. int i = 0;
  4. return () -> x + i;
  5. }
  6. }

makeFun() 返回的 IntSupplier “关住了” ix,因此即使makeFun()已执行完毕,当你调用返回的函数时ix仍然有效,而不是像正常情况下那样在 makeFun() 执行后 ix就消失了。

  • 编译不通过

image.png
xi 的操作都犯了同样的错误:
被 Lambda 表达式引用的局部变量必须是 final 或者是等同 final (变量值没被改变过而实际有了 final 同等的效果)效果的。

  • 编译不通过

image.png
改变一下:
image.png

  • 但与对象类型,只要对应的内存地址不改变就可以 ```java // functional/Closure8.java

import java.util.; import java.util.function.;

public class Closure8 { Supplier> makeFun() { final List ai = new ArrayList<>(); ai.add(1); return () -> ai; } public static void main(String[] args) { Closure8 c7 = new Closure8(); List l1 = c7.makeFun().get(), l2 = c7.makeFun().get(); System.out.println(l1); System.out.println(l2); l1.add(42); l2.add(96); System.out.println(l1); System.out.println(l2); } }


[1] [1] [1, 42] [1, 96]


下面是错误的情况 public class Closure9 { Supplier> makeFun() { List ai = new ArrayList<>(); ai = new ArrayList<>(); // 错误!违反对象的final!!!!!! return () -> ai; } }

  1. - 内部类的情况
  2. ![image.png](https://cdn.nlark.com/yuque/0/2021/png/8429887/1611152381753-ed18c063-e06b-43fa-98f3-f7ae78a734c3.png#align=left&display=inline&height=317&margin=%5Bobject%20Object%5D&name=image.png&originHeight=317&originWidth=449&size=20380&status=done&style=none&width=449)<br />也是不可以的!
  3. <a name="dXfRr"></a>
  4. ## 函数组合
  5. - 多个函数组合成新的函数
  6. | 组合方法 | 支持接口 |
  7. | --- | --- |
  8. | `andThen(argument)`<br />执行原操作,再执行参数操作 | **Function<br />BiFunction<br />Consumer<br />BiConsumer<br />IntConsumer<br />LongConsumer<br />DoubleConsumer<br />UnaryOperator<br />IntUnaryOperator<br />LongUnaryOperator<br />DoubleUnaryOperator<br />BinaryOperator** |
  9. | `compose(argument)`<br />执行参数操作,再执行原操作 | **Function<br />UnaryOperator<br />IntUnaryOperator<br />LongUnaryOperator<br />DoubleUnaryOperator** |
  10. | `and(argument)`<br />原谓词(Predicate)和参数谓词的短路**逻辑与** | **Predicate<br />BiPredicate<br />IntPredicate<br />LongPredicate<br />DoublePredicate** |
  11. | `or(argument)`<br />原谓词和参数谓词的短路**逻辑或** | **Predicate<br />BiPredicate<br />IntPredicate<br />LongPredicate<br />DoublePredicate** |
  12. | `negate()`<br />该谓词的**逻辑非**<br />**<br /> p1.negate() 表示对P1的结果取非 | **Predicate<br />BiPredicate<br />IntPredicate<br />LongPredicate<br />DoublePredicate** |
  13. - Function版本
  14. ```java
  15. public class ProduceFunction {
  16. static Function<String, String>
  17. f1 = s -> {
  18. System.out.println(s);
  19. return s.replace('A', '_');
  20. },
  21. f2 = s -> s.substring(3),
  22. f3 = s -> s.toLowerCase(),
  23. f4 = f1.compose(f2).andThen(f3); //f2->f1->f3
  24. public static void main(String[] args) {
  25. System.out.println(f4.apply("GO AFTER ALL AMBULANCES"));
  26. }
  27. /*
  28. AFTER ALL AMBULANCES
  29. _fter _ll _mbul_nces
  30. */
  31. }

f1 获得字符串时,它已经被f2 剥离了前三个字符。这是因为 compose(f2) 表示 f2 的调用发生在 f1 之前。

  • 谓词Predicate版本 ```java // functional/PredicateComposition.java

import java.util.function.; import java.util.stream.;

public class PredicateComposition { static Predicate p1 = s -> s.contains(“bar”), p2 = s -> s.length() < 5, p3 = s -> s.contains(“foo”), p4 = p1.negate().and(p2).or(p3); public static void main(String[] args) { Stream.of(“bar”, “foobar”, “foobaz”, “fongopuckey”) .filter(p4) .forEach(System.out::println); } }

=== foobar foobaz

  1. `p4` 获取到了所有谓词(`Predicate`)并组合成一个更复杂的谓词。
  2. - 如果字符串中不包含 `bar` 且长度小于 5,或者它包含 `foo` ,则结果为 `true`
  3. <a name="zrnXL"></a>
  4. ## 柯里化
  5. 将一个多参数的函数,转换为一系列单参数函数。
  6. ```java
  7. // functional/CurryingAndPartials.java
  8. import java.util.function.*;
  9. public class CurryingAndPartials {
  10. // 未柯里化:
  11. static String uncurried(String a, String b) {
  12. return a + b;
  13. }
  14. public static void main(String[] args) {
  15. // 柯里化的函数:
  16. Function<String, Function<String, String>> sum =
  17. a -> b -> a + b; // [1]
  18. System.out.println(uncurried("Hi ", "Ho"));
  19. Function<String, String>
  20. hi = sum.apply("Hi "); // [2]
  21. System.out.println(hi.apply("Ho"));
  22. // 部分应用:
  23. Function<String, String> sumHi =
  24. sum.apply("Hup ");
  25. System.out.println(sumHi.apply("Ho"));
  26. System.out.println(sumHi.apply("Hey"));
  27. }
  28. }
  29. ===
  30. Hi Ho
  31. Hi Ho
  32. Hup Ho
  33. Hup Hey

[1] 这一连串的箭头很巧妙。注意,在函数接口声明中,第二个参数是另一个函数。
[2] 柯里化的目的是能够通过提供单个参数来创建一个新函数,所以现在有了一个“带参函数”和剩下的 “自由函数”(free argument) 。
每一级的箭头级联(Arrow-cascading),你都会在类型声明周围包裹另一个 Function
三级

  1. // functional/Curry3Args.java
  2. import java.util.function.*;
  3. public class Curry3Args {
  4. public static void main(String[] args) {
  5. Function<String,
  6. Function<String,
  7. Function<String, String>>> sum =
  8. a -> b -> c -> a + b + c;
  9. Function<String,
  10. Function<String, String>> hi =
  11. sum.apply("Hi ");
  12. Function<String, String> ho =
  13. hi.apply("Ho ");
  14. System.out.println(ho.apply("Hup")); ///Hi Ho Hup
  15. }
  16. }

处理基本类型和装箱时,请使用适当的函数式接口:

  1. // functional/CurriedIntAdd.java
  2. import java.util.function.*;
  3. public class CurriedIntAdd {
  4. public static void main(String[] args) {
  5. IntFunction<IntUnaryOperator>
  6. curriedIntAdd = a -> b -> a + b;
  7. IntUnaryOperator add4 = curriedIntAdd.apply(4);
  8. System.out.println(add4.applyAsInt(5)); ///9
  9. }
  10. }

流式编程