函数式编程
- 使用某种方式操纵其他代码
 - 不用从头开始编写大量代码,而是从易于理解、充分测试及可靠的现有小块开始,最后将它们组合在一起以创建新代码。
 
函数式编程
- 通过合并现有代码来生成新功能而不是从头开始编写所有内容,我们可以更快地获得更可靠的代码。
 - OO(object oriented,面向对象)是抽象数据,FP(functional programming,函数式编程)是抽象行为。
 
如果希望方法在调用时行为不同,该怎么做呢?
只要能将代码传递给方法,我们就可以控制它的行为。
函数式接口:
- 只有一个方法的接口 ```java interface Strategy { String approach(String msg); }
 
以其为模板```javastatic String app(String s) {return s + "?";}public static void main(String[] args) {Strategy[] strategies = new Strategy[]{new Strategy() {@Overridepublic String approach(String msg) {return "zms"; // 匿名内部类}},msg -> msg + "!", // lambdaTriFunctionTest::app // 方法引用};
Lambda表达式
方法引用:
方法引用,它以::为特征。::的左边是类或对象的名称, ::的右边是方法的名称,但是没有参数列表。
方法引用组成:类名或对象名,后面跟 ::,然后跟方法名称。
public class TriFunctionTest {interface Callable { // [1]void call(String s);}public void func(String s) {System.out.println("TriFunctionTest.func");}public static void funStatic(String s) {System.out.println("TriFunctionTest.funStatic");}public static void main(String[] args) {Callable callable1 = new TriFunctionTest()::func;Callable callable2 = TriFunctionTest::funStatic;callable1.call("zms");callable2.call("zms");/*** TriFunctionTest.func* TriFunctionTest.funStatic*/}}
- interface和引用的方法的名字不重要,重要的是返回值和参数是不是匹配
 - 静态方法引用不需要实例化类对象
 - 引用非静态方法需要实例化对象
 
基本特征
- 参数和函数体被箭头
->分隔开: msg -> msg + “!” - 当只用一个参数,可以不需要括号 
() 如果没有参数,则必须使用括号 () 表示空参数列表。对于多个参数,将参数列表放在括号 () 中。- 单行的Lambda不使用return
 - 多行的必须使用{}+return
 
Lambda的例子
public class TriFunctionTest {interface ClassA {String A(String a);}interface ClassB {String B(String a, Double b);}public static void main(String[] args) {ClassA classA = str -> str + "!";System.out.println(classA.A("zms"));ClassB classB = (str, b) -> str + b + "?";System.out.println(classB.B("zms", 88.0));}}zms!zms88.0?
Lambda递归
递归方法必须是成员变量或静态变量,否则会出现编译时错误。 
1. 静态变量的情况
public class TriFunctionTest {private static Fab f;interface Fab {int fab(int n);}public static void main(String[] args) {f = n -> n == 0 ? 1 : n == 1 ? 1 : f.fab(n - 1) + f.fab(n - 2);for (int i = 0; i < 10; i++) {System.out.println(f.fab(i));}}}
成员变量
public class TriFunctionTest {Fab f;interface Fab {int fab(int n);}public static void main(String[] args) {TriFunctionTest triFunctionTest = new TriFunctionTest();triFunctionTest.f = n ->n == 0 ? 1 :n == 1 ? 1 :triFunctionTest.f.fab(n - 1) + triFunctionTest.f.fab(n - 2);for (int i = 0; i < 10; i++) {System.out.println(triFunctionTest.f.fab(i));}}}
Runnable接口
接口的方法不带参数以及返回值
public abstract void run();public static void main(String[] args) {Runnable runnable = () -> {System.out.println("TriFunctionTest.main");};runnable.run();}
例子2:
public static void main(String[] args) {new Thread(() -> System.out.println("TriFunctionTest.main")).start();new Thread(() -> {System.out.println("zms");System.out.println("vms");}).start();// 也可以使用方法引用作为作为参数传入Thread}
未绑定的方法引用
指没有关联对象的非静态方法.
使用未绑定的引用时,我们必须先提供对象:public class TriFunctionTest {static class X {String f() {return "X::F";}}interface MakeString {String make();}interface TransformX {String transform(X x);}public static void main(String[] args) {/*MakeString makeString = X::f; // 不能直接赋值,因为f()不是静态方法*/TransformX transformX = X::f;X x = new X();System.out.println(transformX.transform(x));}}
引用f()时候需要使用this对象,因为f()不是静态的方法,必须有一个对象对他进行调用.
拿到未绑定的方法引用,并且调用它的transform()方法,将一个X类的对象传递给它,最后使得x.f()以某种方式被调用。Java知道它必须拿第一个参数,该参数实际就是this对象,然后对此调用方法。
如果你的方法有更多个参数,就以第一个参数接受this的模式来处理。
public class TriFunctionTest {static class X {String f(int a) {return "X::F" + a;}}interface MakeString {String make();}interface TransformX {String transform(X x, int a);// 自动将a传给了x对象}public static void main(String[] args) {/*MakeString makeString = X::f; // 不能直接赋值,因为f()不是静态方法*/TransformX transformX = X::f;X x = new X();System.out.println(transformX.transform(x,1)); // X::F1}}
构造函数引用
构造函数只有一个相同名称:
:: 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(); }
interface Make1Arg {Dog make(String nm);}interface Make2Args {Dog make(String nm, int age);}public static void main(String[] args) {MakeNoArgs noArgs = Dog::new;Make1Arg make1Arg = Dog::new;Make2Args make2Args = Dog::new;System.out.println(noArgs.make());System.out.println(make1Arg.make("zms"));System.out.println(make2Args.make("zms", 11));
// fp.Dog@6d03e736 // fp.Dog@568db2f2 // fp.Dog@378bf509 } }
<a name="ZAz7O"></a>## 函数式接口- 方法引用和 Lambda 表达式都必须被赋值,同时赋值需要类型信息才能使编译器保证类型的正确性。- Lambda 表达式包含 类型推导 (编译器会自动推导出类型信息,避免了程序员显式地声明)。编译器必须能够以某种方式推导出 x 的类型。- 当 Lambda 表达式被赋值时,编译器必须确定 x 和 y 的确切类型以生成正确的代码。
(x, y) -> x + y
- 使用 `@`**`FunctionalInterface` **注解强制执行此“函数式方法”模式:- 每个接口只包含**一个抽象方法**,称为 _函数式方法_ 。```java// functional/FunctionalAnnotation.java@FunctionalInterfaceinterface Functional {String goodbye(String arg);}interface FunctionalNoAnn {String goodbye(String arg);}/*@FunctionalInterfaceinterface NotFunctional {String goodbye(String arg);String hello(String arg);}产生错误信息:函数式方法必须只有一个抽象函数*/public class FunctionalAnnotation {public String goodbye(String arg) {return "Goodbye, " + arg;}public static void main(String[] args) {FunctionalAnnotation fa =new FunctionalAnnotation();Functional f = fa::goodbye;FunctionalNoAnn fna = fa::goodbye;// Functional fac = fa; //错误!FunctionalAnnotation 并没有显式地去实现 Functional 接口。Functional fl = a -> "Goodbye, " + a;FunctionalNoAnn fnal = a -> "Goodbye, " + a;}}
自带的函数接口
命名规则
- 处理的是对象而不是基本类型
FunctionConsumerPredicate
 - 接受的参数是基本类型,由名称的第一部分进行标识
LongConsumerDoubleFunctionIntPredicate- 返回基本类型的Supplier(无参数返回任意类型)除外
 
 - 如果返回值是基本类型,用
To表示ToLongFunction<T>IntToLongFunction
 - 返回类型和参数类型相同
OperatorUnaryOPerator单个参数BinaryOperator两个参数
 - 接受参数并返回boolean使用谓词
Predicate - 接受的两个参数类型不同,名称中有一个
Bi 
系统接口
| 特征 | 函数式方法名 | 示例 | 
|---|---|---|
| 无参数; 无返回值  | 
Runnable (java.lang) run() | 
Runnable | 
| 无参数; 返回类型任意  | 
Supplierget()getAs类型() | 
Supplier<T>BooleanSupplier IntSupplier LongSupplier DoubleSupplier  | 
| 无参数; 返回类型任意  | 
Callable (java.util.concurrent) call() | 
Callable<V> | 
| 1 参数; 无返回值  | 
Consumeraccept() | 
Consumer<T>IntConsumer LongConsumer DoubleConsumer  | 
| 2 参数 Consumer | BiConsumeraccept() | 
BiConsumer<T,U> | 
| 2 参数 Consumer; 第一个参数是 引用; 第二个参数是 基本类型  | 
Obj类型Consumeraccept() | 
ObjIntConsumer<T>ObjLongConsumer<T>ObjDoubleConsumer<T> | 
| 1 参数; 返回类型不同  | 
Functionapply()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 参数; 返回类型相同  | 
UnaryOperatorapply() | 
UnaryOperator<T>IntUnaryOperator LongUnaryOperator DoubleUnaryOperator  | 
| 2 参数,类型相同; 返回类型相同  | 
BinaryOperatorapply() | 
BinaryOperator<T>IntBinaryOperator LongBinaryOperator DoubleBinaryOperator  | 
| 2 参数,类型相同; 返回整型  | 
Comparator (java.util) compare() | 
Comparator<T> | 
| 2 参数; 返回布尔型  | 
Predicatetest() | 
Predicate<T>BiPredicate<T,U>IntPredicate LongPredicate DoublePredicate  | 
| 参数基本类型; 返回基本类型  | 
类型To类型FunctionapplyAs类型() | 
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变体实例
// functional/FunctionVariants.javaimport java.util.function.*;class Foo {}class Bar {Foo f;Bar(Foo f) { this.f = f; }}class IBaz {int i;IBaz(int i) {this.i = i;}}class LBaz {long l;LBaz(long l) {this.l = l;}}class DBaz {double d;DBaz(double d) {this.d = d;}}public class FunctionVariants {static Function<Foo,Bar> f1 = f -> new Bar(f);static IntFunction<IBaz> f2 = i -> new IBaz(i);static LongFunction<LBaz> f3 = l -> new LBaz(l);static DoubleFunction<DBaz> f4 = d -> new DBaz(d);static ToIntFunction<IBaz> f5 = ib -> ib.i;static ToLongFunction<LBaz> f6 = lb -> lb.l;static ToDoubleFunction<DBaz> f7 = db -> db.d;static IntToLongFunction f8 = i -> i;static IntToDoubleFunction f9 = i -> i;static LongToIntFunction f10 = l -> (int)l;static LongToDoubleFunction f11 = l -> l;static DoubleToIntFunction f12 = d -> (int)d;static DoubleToLongFunction f13 = d -> (long)d;public static void main(String[] args) {Bar b = f1.apply(new Foo());IBaz ib = f2.apply(11);LBaz lb = f3.apply(11);DBaz db = f4.apply(11);int i = f5.applyAsInt(ib);long l = f6.applyAsLong(lb);double d = f7.applyAsDouble(db);l = f8.applyAsLong(12);d = f9.applyAsDouble(12);i = f10.applyAsInt(12);d = f11.applyAsDouble(12);i = f12.applyAsInt(13.0);l = f13.applyAsLong(13.0);}}
这些 Lambda 表达式尝试生成适合函数签名的最简代码。 在某些情况下有必要进行强制类型转换,否则编译器会报截断错误。
使用函数接口时,名称无关紧要——只要参数类型和返回类型相同。 Java 会将你的方法映射到接口方法。 要调用方法,可以调用接口的函数式方法名,例Runnable的run(),而不是你的方法名。
// functional/ClassFunctionals.javaimport java.util.*;import java.util.function.*;class AA {}class BB {}class CC {}public class ClassFunctionals {static AA f1() { return new AA(); }static int f2(AA aa1, AA aa2) { return 1; }static void f3(AA aa) {}static void f4(AA aa, BB bb) {}static CC f5(AA aa) { return new CC(); }static CC f6(AA aa, BB bb) { return new CC(); }static boolean f7(AA aa) { return true; }static boolean f8(AA aa, BB bb) { return true; }static AA f9(AA aa) { return new AA(); }static AA f10(AA aa1, AA aa2) { return new AA(); }public static void main(String[] args) {Supplier<AA> s = ClassFunctionals::f1;s.get();Comparator<AA> c = ClassFunctionals::f2;c.compare(new AA(), new AA());Consumer<AA> cons = ClassFunctionals::f3;cons.accept(new AA());BiConsumer<AA,BB> bicons = ClassFunctionals::f4;bicons.accept(new AA(), new BB());Function<AA,CC> f = ClassFunctionals::f5;CC cc = f.apply(new AA());BiFunction<AA,BB,CC> bif = ClassFunctionals::f6;cc = bif.apply(new AA(), new BB());Predicate<AA> p = ClassFunctionals::f7;boolean result = p.test(new AA());BiPredicate<AA,BB> bip = ClassFunctionals::f8;result = bip.test(new AA(), new BB());UnaryOperator<AA> uo = ClassFunctionals::f9;AA aa = uo.apply(new AA());BinaryOperator<AA> bo = ClassFunctionals::f10;aa = bo.apply(new AA(), new AA());}}
每个方法名称都是随意的(如 f1(),f2()等)。正如你刚才看到的,一旦将方法引用赋值给函数接口,我们就可以调用与该接口关联的函数方法。 在此示例中为 get()、compare()、accept()、apply() 和 test()。
自定义接口
接口是有限的。比如有 BiFunction,但也仅此而已。 如果需要三参数函数的接口怎么办? 其实这些接口非常简单,很容易查看 Java 库源代码并自行创建。
@FunctionalInterfacepublic interface TriFunction<T, U, V, R> {R apply(T t, U u, V v);}
// functional/TriFunctionTest.javapublic class TriFunctionTest {static int f(int i, long l, double d) { return 99; }public static void main(String[] args) {TriFunction<Integer, Long, Double, Integer> tf =TriFunctionTest::f; // 使用引用进行赋值tf = (i, l, d) -> 12; // 使用lambda进行赋值}}
高阶函数
- 消费或者产生函数的函数
 
产生函数
public class ProduceFunction {interface FuncSS extends Function<String, String> {}static FuncSS produce() {//返回一个FuncSS,所以下面的s可以知道是Stringreturn s -> s.toLowerCase();}public static void main(String[] args) {FuncSS funcSS = produce();System.out.println(funcSS.apply("VMS"));}}
produce()是高阶函数。
class Two { }
public class ProduceFunction {
    static Two consume(Function
public static void main(String[] args) {System.out.println(consume(one -> new Two()));}
}
基于消费函数生成新函数:```javaclass I {@Overridepublic String toString() {return "I";}}class O {@Overridepublic String toString() {return "O";}}public class ProduceFunction {static Function<I, O> transform(Function<I, O> in) {return in.andThen(o -> {System.out.println(o);return o;});}public static void main(String[] args) {Function<I, O> f = transform(i -> {System.out.println(i);return new O();});O o = f.apply(new I());}}
使用到了 Function 接口中名为 andThen() 的默认方法,该方法专门用于操作函数。
在调用 in 函数之后调用 andThen().
要附加一个 andThen() 函数,我们只需将该函数作为参数传递。 transform() 产生的是一个新函数,它将 in 的动作与 andThen() 参数的动作结合起来。
闭包
如果一个lambda使用了作用域之外的变量,返回该函数会发生什么?
Lambda 可以没有限制地引用 实例变量和静态变量。但 局部变量必须显式声明为final,或事实上是final 。
引用外部变量
// functional/Closure1.javaimport java.util.function.*;public class Closure1 {int i; // 对于函数来说是一个外部变量IntSupplier makeFun(int x) {return () -> x + i++;}}
如果你对同一个对象多次调用 makeFun() ,你最终会得到多个函数,它们共享 i 的存储空间:
class Closure1 {int i;IntSupplier makeFun(int x) {return () -> x + i++;}}public class ProduceFunction {public static void main(String[] args) {Closure1 closure1 = new Closure1();IntSupplier f1 = closure1.makeFun(0);IntSupplier f2 = closure1.makeFun(0);IntSupplier f3 = closure1.makeFun(0);System.out.println(f1.getAsInt());System.out.println(f2.getAsInt());System.out.println(f3.getAsInt());/*012*/}}
每次调用 getAsInt() 都会增加 i,表明存储是共享的。
引用局部变量
class Closure1 {IntSupplier makeFun(int x) {int i = 0;return () -> x + i;}}
makeFun() 返回的 IntSupplier “关住了” i 和 x,因此即使makeFun()已执行完毕,当你调用返回的函数时i 和 x仍然有效,而不是像正常情况下那样在 makeFun() 执行后 i 和x就消失了。
- 编译不通过
 

x 和 i 的操作都犯了同样的错误:
被 Lambda 表达式引用的局部变量必须是 final 或者是等同 final (变量值没被改变过而实际有了 final 同等的效果)效果的。
- 编译不通过
 

改变一下:
- 但与对象类型,只要对应的内存地址不改变就可以 ```java // functional/Closure8.java
 
import java.util.; import java.util.function.;
public class Closure8 {
  Supplier> makeFun() {
    final List
[1] [1] [1, 42] [1, 96]
下面是错误的情况
public class Closure9 {
  Supplier> makeFun() {
    List
- 内部类的情况<br />也是不可以的!<a name="dXfRr"></a>## 函数组合- 多个函数组合成新的函数| 组合方法 | 支持接口 || --- | --- || `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** || `compose(argument)`<br />执行参数操作,再执行原操作 | **Function<br />UnaryOperator<br />IntUnaryOperator<br />LongUnaryOperator<br />DoubleUnaryOperator** || `and(argument)`<br />原谓词(Predicate)和参数谓词的短路**逻辑与** | **Predicate<br />BiPredicate<br />IntPredicate<br />LongPredicate<br />DoublePredicate** || `or(argument)`<br />原谓词和参数谓词的短路**逻辑或** | **Predicate<br />BiPredicate<br />IntPredicate<br />LongPredicate<br />DoublePredicate** || `negate()`<br />该谓词的**逻辑非**<br />**<br /> p1.negate() 表示对P1的结果取非 | **Predicate<br />BiPredicate<br />IntPredicate<br />LongPredicate<br />DoublePredicate** |- Function版本```javapublic class ProduceFunction {static Function<String, String>f1 = s -> {System.out.println(s);return s.replace('A', '_');},f2 = s -> s.substring(3),f3 = s -> s.toLowerCase(),f4 = f1.compose(f2).andThen(f3); //f2->f1->f3public static void main(String[] args) {System.out.println(f4.apply("GO AFTER ALL AMBULANCES"));}/*AFTER ALL AMBULANCES_fter _ll _mbul_nces*/}
当 f1 获得字符串时,它已经被f2 剥离了前三个字符。这是因为 compose(f2) 表示 f2 的调用发生在 f1 之前。
- 谓词Predicate版本 ```java // functional/PredicateComposition.java
 
import java.util.function.; import java.util.stream.;
public class PredicateComposition {
  static Predicate
=== foobar foobaz
`p4` 获取到了所有谓词(`Predicate`)并组合成一个更复杂的谓词。- 如果字符串中不包含 `bar` 且长度小于 5,或者它包含 `foo` ,则结果为 `true`。<a name="zrnXL"></a>## 柯里化将一个多参数的函数,转换为一系列单参数函数。```java// functional/CurryingAndPartials.javaimport java.util.function.*;public class CurryingAndPartials {// 未柯里化:static String uncurried(String a, String b) {return a + b;}public static void main(String[] args) {// 柯里化的函数:Function<String, Function<String, String>> sum =a -> b -> a + b; // [1]System.out.println(uncurried("Hi ", "Ho"));Function<String, String>hi = sum.apply("Hi "); // [2]System.out.println(hi.apply("Ho"));// 部分应用:Function<String, String> sumHi =sum.apply("Hup ");System.out.println(sumHi.apply("Ho"));System.out.println(sumHi.apply("Hey"));}}===Hi HoHi HoHup HoHup Hey
[1] 这一连串的箭头很巧妙。注意,在函数接口声明中,第二个参数是另一个函数。
[2] 柯里化的目的是能够通过提供单个参数来创建一个新函数,所以现在有了一个“带参函数”和剩下的 “自由函数”(free argument) 。
每一级的箭头级联(Arrow-cascading),你都会在类型声明周围包裹另一个 Function 。
三级
// functional/Curry3Args.javaimport java.util.function.*;public class Curry3Args {public static void main(String[] args) {Function<String,Function<String,Function<String, String>>> sum =a -> b -> c -> a + b + c;Function<String,Function<String, String>> hi =sum.apply("Hi ");Function<String, String> ho =hi.apply("Ho ");System.out.println(ho.apply("Hup")); ///Hi Ho Hup}}
处理基本类型和装箱时,请使用适当的函数式接口:
// functional/CurriedIntAdd.javaimport java.util.function.*;public class CurriedIntAdd {public static void main(String[] args) {IntFunction<IntUnaryOperator>curriedIntAdd = a -> b -> a + b;IntUnaryOperator add4 = curriedIntAdd.apply(4);System.out.println(add4.applyAsInt(5)); ///9}}
