【Java】《OnJava8》笔记——第13章函数式编程

《On Java 8》中文版,又名《Java编程思想》 第5版
接下来的都是个人学习过程的笔记,不是总结,没有参考价值,但是这本书很棒

方法引用

未绑定方法

package chapter13;

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) {
        TransformX sp=X::f;
//        简单说,就是告诉sp在调用transform的时候,当你接受了一个X对象的时候,调用其f方法
        X x=new X();
        MakeString ms=x::f;//也work
        TransformX sp1=(a)->a.f();//do the same
        System.out.println(sp.transform(x));
        System.out.println(x.f());
    }
}

刚开始没有理解上面这段代码的运作流程,实际上就是就是告诉sp在调用transform的时候,当你接受了一个X对象的时候,调用其f方法,上面代码里sp1sp的生成过程一毛一样,这两个TransformX的功能完全相同。这也就是说,X::f会生成一个TransformX类型的对象,这个对象的String transform(X x)方法长成下面的这个样子

String transform(X x){
	return x.f();
}

更进一步,看下面代码

class X{
    String f(){
        return "X::f()";
    }
    String f2(String s){
        return s+"!";
    }
}

interface MyTransformX{
    String transform(X x,String s);
}

public class UnboundMethodReference {
    public static void main(String[] args) {
        MyTransformX mySp=X::f2;
        System.out.println(mySp.transform(x,"hi"));
    }
}

X::f2会生成一个MyTransformX类型的对象,这个对象的String transform(X x)方法长成下面的这个样子

String transform(X x,String s){
	return x.f2(s);
}

函数式接口

// functional/FunctionVariants.java

import 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);
  }
}

上面的代码在构造的时候,其实可以用构造函数引用的,例如static Function<Foo,Bar> f1 = Bar::new;

package chapter13;

import java.util.function.*;

class In1 {
}

class In2 {
}

public class MethodConversion {
    static void accept(In1 i1, In2 i2) {
        System.out.println("accept()");
    }

    void boundAccept(In1 i1, In2 i2) {
        System.out.println("boundAccept()");
    }

    static void someOtherName(In1 i1, In2 i2) {
        System.out.println("someOtherName()");
    }

    public static void main(String[] args) {
        BiConsumer<In1, In2> bic;
        bic = MethodConversion::accept;
        bic.accept(new In1(), new In2());

        bic = MethodConversion::someOtherName;
        bic.accept(new In1(), new In2());

        MethodConversion m = new MethodConversion();
        bic=m::boundAccept;
        bic.accept(new In1(), new In2());
    }
}

这段代码我增加了一点,注意main函数里的第一个bic,生成的时候用的是MethodConversion::accept,但这个时候应该不算是非绑定引用吧,因为这个accept是个静态方法,可以由类直接调用的。跟最后面那个m::boundAccept其实差别不大。

高阶函数

package chapter13;

import java.util.function.*;

interface FuncSS extends Function<String,String>{}

public class ProduceFunction {
    static FuncSS produce(){
        return String::toUpperCase;
//        return s->s.toUpperCase();
    }


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

第一段代码也可以直接用String::toUpperCase的非绑定方法,因为Function接口需要实现一个R apply(T t)功能

// functional/TransformFunction.java

import java.util.function.*;

class I {
  @Override
  public String toString() { return "I"; }
}

class O {
  @Override
  public String toString() { return "O"; }
}

public class TransformFunction {
  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> f2 = transform(i -> {
      System.out.println(i);
      return new O();
    });
    O o = f2.apply(new I());
  }
}

andThenFunction<T, R>接口里的一个default方法,具体实现如下,顾名思义,接受一个Function<? super R, ? extends V>返回一个Function<T, V>,当调用返回的对象的apply(T t)方法的时候:先会执行Function<T, R>apply返回一个R类型对象,然后这个对象作为参数调用Function<? super R, ? extends V>apply(R r)返回一个V类型的对象。所以上面的代码就容易理解了,唯一需要说的就是,in.andThen方法传入的是一个Function<O, O>对象

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

闭包

从 Lambda 表达式引用的局部变量必须是 final 或者是等同 final 效果的。
这就叫做等同 final 效果(Effectively Final)。这个术语是在 Java 8 才开始出现的,表示虽然没有明确地声明变量是 final 的,但是因变量值没被改变过而实际有了 final 同等的效果。 如果局部变量的初始值永远不会改变,那么它实际上就是 final 的。
等同 final 效果意味着可以在变量声明前加上 final 关键字而不用更改任何其余代码。 实际上它就是具备 final 效果的,只是没有明确说明。

package chapter13;

import java.util.function.IntSupplier;

public class Closure1 {
    int i;
    IntSupplier makeFun(int x){
        return ()->x+i++;
    }
}

makeFun被调用的时候,这个i是会被jvm搜索的,这也就是说,jvm一直要持有一个i的对象,书上是这么解释的

实际上,垃圾收集器几乎肯定会保留一个对象,并将现有的函数以这种方式绑定到该对象上

函数组合

// functional/FunctionComposition.java

import java.util.function.*;

public class FunctionComposition {
  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);
  public static void main(String[] args) {
    System.out.println(
      f4.apply("GO AFTER ALL AMBULANCES"));
  }
}

上面的代码使用了composeandThen,这两个方法的功能刚好相反

柯里化和部分求值

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值