函数式编程
Lambda表达式¶
-
简化函数式接口的使用
// 定义一个函数式接口 @FunctionalInterface interface MyFunction { int apply(int a, int b); } public class LambdaExample { public static void main(String [] args) { // 使用 Lambda 表达式实现函数式接口 MyFunction add = (a, b) -> a + b; MyFunction subtract = (a, b) -> a - b; // 调用函数对象 int result1 = add.apply(5, 3); int result2 = subtract.apply(5, 3); System.out.println("Addition result: " + result1); System.out.println("Subtraction result: " + result2); } }
-
()->{}
- 只有一个参数时可以省略(),没有参数时不可以省略
-
只有一行代码时可以省略{}以及return
-
递归函数
fact = n->n==0?1:n*fact.call(n-1)
- 对于lambda变量使用call进行调用
方法引用¶
- 类名或对象名+::+方法名
-
自然,非静态方法使用对象名,静态方法使用类名
interface Callable { // [1] void call(String s); } class Describe { void show(String msg) { // [2] System.out.println(msg); } } public class MethodReferences { static void hello(String name) { // [3] System.out.println("Hello, " + name); } static class Description { String about; Description(String desc) { about = desc; } void help(String msg) { // [4] System.out.println(about + " " + msg); } } static class Helper { static void assist(String msg) { // [5] System.out.println(msg); } } public static void main(String [] args) { Describe d = new Describe(); Callable c = d:: show; // [6] c.call("call()"); // [7] c = MethodReferences:: hello; // [8] c.call("Bob"); c = new Description("valuable"):: help; // [9] c.call("information"); c = Helper:: assist; // [10] c.call("Help!"); } }
-
可以使用具有和接口相同签名(参数类型和返回值) 的方法引用来实现接口。
-
Thead对象创建要求传入实现Runnable接口的对象
new Thread(Go:: go).start();
-
未绑定的方法引用
- 对于非静态方法不能直接通过类访问,因为这缺少 this 参数(要先创建对象,然后通过对象访问)
class X { String f() { return "X:: f()"; } } interface MakeString { String make(); } interface TransformX { String transform(X x); } public class UnboundMethodReference { public static void main(String [] args) { // MakeString ms = X:: f; // [1] TransformX sp = X:: f; X x = new X(); System.out.println(sp.transform(x)); // [2] System.out.println(x.f()); // Same effect } }
-
如果接口参数多一个对象类型,就会自动创建对象!
-
构造器方法引用
- 使用参数匹配的接口捕获不同的构造方法
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; } } interface MakeNoArgs { Dog make(); } interface Make1Arg { Dog make(String nm); } interface Make2Args { Dog make(String nm, int age); } public class CtorReference { public static void main(String [] args) { //名字都是 new MakeNoArgs mna = Dog:: new; // [1] Make1Arg m1a = Dog:: new; // [2] Make2Args m2a = Dog:: new; // [3] Dog dn = mna.make(); Dog d1 = m1a.make("Comet"); Dog d2 = m2a.make("Ralph", 4); } }
函数式接口¶
- lambda表达式是函数式接口的实例,是与其关联的目标类型(即函数式接口描述lambda的类型,而lambda是特定类型函数式接口的一个具体实例)
- 每个 Lambda 表达式都能隐式地赋值给函数式接口
Runnable r = () -> System.out.println("hello world");
- 函数式接口是只包含一个抽象方法声明的接口
-
可以使用
@FunctionalInterface
(可选的,会检查是否满足只有一个抽象方法的条件) -
内置函数接口类型(java.util.function)
-
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; 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);
- 使用apply、get、compare等方法调用(见图片)
- 方法引用也可以使用函数是接口
高阶函数¶
- 把函数作为参数或返回值的函数
- 需要配合函数式接口使用
- 把函数作为返回值
//可以对内置的函数式接口进行重命名(通过继承) interface FuncSS extends Function <String, String> {} public class ProduceFunction { static FuncSS produce() { return s -> s.toLowerCase();//函数作为返回值 } public static void main(String [] args) { FuncSS f = produce(); System.out.println(f.apply("YELLING")); } }
闭包¶
- 如果一个lambda使用了函数作用域之外的变量,解决这个问题就是支持闭包
- 如果一个 lambda 表达式中包含了局部变量,这个变量必须为 final(至少不会被修改(即实际上的 final 变量))
- 也可以将局部变量赋值给final变量再交给lambda表达式处理
- 引用类型的是可以修改指向的对象的(设计final原理),但是包装类型不可以
- 将内部类作为返回值时也需要注意类似的问题
杂项¶
- 函数组合
- 将多个函数结合使用
return in.andThen(o -> {System.out.println(o);return o;});
f.andThen()
在f之后调用.compose()
在f之前调用- 会得到一个新函数
-
//函数组合 f4 = fa.compose(f2).andThen(f3); //逻辑 public class PredicateComposition { static Predicate <String> 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); } } //不包含并且 或者...
-
柯里化和部分求值
- 将一个接受多个参数的函数转变为一系列只接受一个参数的函数
- 每接收一个新参数就得到一个新函数,直至得到结果
import 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 "); sum.apply("Hup ").apply("Ho ").apply("Hup"); System.out.println(ho.apply("Hup")); } }