13.函数式编程

函数式编程

13.3方法引用
13.3.2未绑定的方法引用

未绑定的方法引用是指没有关联对象的普通(非静态)方法。使用未绑定的引用时,必须先提供对象:

public class UnboundMethodReference {
    public static void main(String[] args) {
        // 即使make和f具有相同的签名,编译也会报错。这是因为实际上还有另一个隐藏的参数:this。
        // 不能在没有X对象的前提下调用f。因此,X::f表示未绑定的方法引用,因为它尚未绑定到对象
        // MakeString ms = X::f;

        // 使用未绑定的引用时,函数式方法的签名不再与方法引用的签名完全匹配。
        // 原因是:需要一个对象来调用方法
        TransformX sp = X::f;
        X x = new X();

        System.out.println(sp.transform(x));
        System.out.println(x.f());
    }
}

class X {
    String f() {
        return "X::f()";
    }
}

interface MakeString {
    String make();
}

interface TransformX {
    String transform(X x);
}

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

public class MultiUnbound {
    public static void main(String[] args) {
        TwoArgs twoArgs = This::two;
        ThreeArgs threeArgs = This::three;
        FourArgs fourArgs = This::four;
        This athis = new This();
        twoArgs.call2(athis, 11, 3.14);
        threeArgs.call3(athis, 11, 3.14, "Three");
        fourArgs.call4(athis, 11, 3.14, "Four", 'Z');
    }
}

class This {
    void two(int i, double d) {}

    void three(int i, double d, String s) {}

    void four(int i, double d, String s, char c) {}
}

interface TwoArgs {
    void call2(This athis, int i, double d);
}

interface ThreeArgs {
    void call3(This athis, int i, double d, String s);
}

interface FourArgs {
    void call4(This athis, int i, double d, String s, char c);
}
13.4函数式接口

基本命名准则:

  1. 如果只处理对象而非基本类型,名称则为FunctionConsumerPredicate等,参数类型通过泛型添加。
  2. 如果接收的参数是基本类型,则由名称的第一部分表示,如LongConsumerDoubleFunctionIntPredicate等,但返回基本类型的Supplier接口例外。
  3. 如果返回值为基本类型,则用To表示,如ToLongFunction<T>IntToLongFunction
  4. 如果返回值类型与参数类型一致,则是一个运算符:单个参数使用UnaryOperator,两个参数使用BinaryOperator
  5. 如果接收两个参数且返回值为布尔值,则名称中有一个Bi
13.5高阶函数

高阶函数只是一个消费或产生函数的函数。

public class ProduceFunction {
    // 使用lambda表达式,可以轻松地在方法中创建和返回一个函数
    static FuncSS produce() {
        return s -> s.toLowerCase();
    }

    public static void main(String[] args) {
        FuncSS f = produce();
        System.out.println(f.apply("YELLING"));
    }
}

// 使用继承,可以轻松地为专用接口创建别名
interface FuncSS extends Function<String, String> {}
13.6闭包
public class Closure1 {
    int i;

    // 调用makeFun之前需要先创建对象,当对同一个对象多次调用makeFun,
    // 最终会得到多个函数,它们共享i的存储空间
    IntSupplier makeFun(int x) {
        return () -> x + i++;
    }
}

public class SharedStorage {
    public static void main(String[] args) {
        Closure1 c1 = new Closure1();
        IntSupplier f1 = c1.makeFun(0);
        IntSupplier f2 = c1.makeFun(0);
        IntSupplier f3 = c1.makeFun(0);
        System.out.println(f1.getAsInt());
        System.out.println(f2.getAsInt());
        System.out.println(f3.getAsInt());
    }
}

被lambda表达式引用的局部变量必须是final或者是等同final效果的,即虽然没有明确地声明变量是final的,但是因变量值没被改变过而实际有了final同等的效果,如果局部变量的初始值永远不会改变,那么它实际上就是final的。

如果函数式方法中使用的外部局部变量是引用,而不是基本类型的话:

public class Closure7 {
    IntSupplier makeFun(int x) {
        Integer i = 0;
        // 编译器识别到变量i的值被更改过
        i = i + 1;
        return () -> x + i;
    }
}
public class Closure8 {
    // 每次调用makeFun时,其实都会创建并返回一个全新而非共享的ArrayList,
    // 也就是说,每个闭包都有自己独立的ArrayList,它们之间互不干扰
    Supplier<List<Integer>> makeFun() {
        final List<Integer> ai = new ArrayList<>();
        // 改变List的内容却没产生编译时错误
        ai.add(1);
        return () -> ai;
    }

    public static void main(String[] args) {
        Closure8 c7 = new Closure8();
        List<Integer> l1 = c7.makeFun().get();
        List<Integer> l2 = c7.makeFun().get();
        l1.add(42);
        l2.add(96);
        System.out.println(l1);
        System.out.println(l2);
    }
}
13.8柯里化和部分求值

柯里化意为:将一个多参数的函数,转换为一系列单参数函数。

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;
        System.out.println(uncurried("Hi", "Ho"));

        Function<String, String> hi = sum.apply("Hi ");
        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 Ho
// Hi Ho
// Hup Ho
// Hup Hey
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

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

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值