Java 8 函数式接口 | 8月更文挑战

653 阅读4分钟

Java 8 新增的函数式接口是个令人惊喜的特性。Java是一门面对对象的语言,在Java中对象是一等公民,函数无法作为另一个函数的参数。而函数式接口提供了将函数作为参数能力,将函数作为参数参与另一个函数的执行,让函数拥有更强的灵活性。

纯函数

纯函数有两个特性不可变对象无副作用不可变对象无副作用特性保证函数执行结果的稳定

  • 不可变对象:除参数外,函数执行所使用的数据(对象)是固定不变的 不可变对象保证了参数不变时,函数执行的结果不变。例如函数y = x + 1,参数x不变时,结果y也不会改变。而如果将常量1换成从数据库查询,即y = x + db(x) 那么由于db(x)查询结果的不固定,这个函数y = x + db(x)不具有不可变对象特性
  • 无副作用:函数不会修改外部数据或对象 无副作用在并发编程时很有用,因为不会修改外部对象,在使用时不用考虑多线程执行顺序不固定时出现意外的结果

函数副作用:指当调用函数时会产生附加的影响。例如修改全局变量或修改参数

Lambda表达式

Lambda表达式的意思是匿名函数,即Lambda表达式产生是函数,而不是类

Lambda的结构为(参数) -> {}, ->表示传递执行, {}表示函数体(函数的执行逻辑),例如:

Function<Integer, Integer> add = (Integer x) -> {
    return x + 1;
};

Function<Integer, Integer> add1 = (Integer x) -> x + 1;
  • 当只用一个参数,可以不需要括号()
  • 如果没有参数,则必须使用括号() 表示空参数列表
  • 对于多个参数,将参数列表放在括号()
  • 当函数体只有一行时,可以不写return

Lambda很重要的一个作用就是取待匿名对象的实现,例如:

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("hello");
    }
});

Thread thread1 = new Thread(() -> System.out.println("hello"));

方法引用

方法引用表示一个函数, 通过类名或实例名::方法名称的方式定义一个函数,例如;

public void test() {

    Function<Integer, Integer> add = this::add;
}

private Integer add(int x){
    return x + 1;
}

函数式接口

Java 8中满足只包含一个抽象方法的接口称为函数式接口。通常使用@FunctionalInterface 注解显示声明该接口是函数式接口。例如:

@FunctionalInterface
public interface AddFunction<T, R> {

    Integer add(T t, R r);
}

java.util.function包中系统提供了一些基本的函数式接口,如下图: image.png 其中最常用的有Function<T, R>Supplier<T>Consumer<T>Predicate<T>

Function<T, R>

Function<T, R>: 表示接受一个参数并返回结果的函数。 即接收一个T类型的参数,返回一个R类型的结果,函数定义如下图: image.png 任何符合该形式的函数都可以看作Function<T, R>的实现类,调用apply方法执行该函数,如下图: image.png

函数式接口中可以存在默认方法静态方法Function<T, R>中提供了两个组合方法composeandThen

  • compose:先执行参数操作,再执行原操作 compose接收接收一个Function<T, R>作为参数,并返回一个组合后的的新函数。该参数(Function)的执行结果作为原函数的参数执行 image.png 示例: image.png -andThen:先执行原操作,再执行参数操作 andThen同样接收接收一个Function<T, R>作为参数,返回一个组合后的的新函数。但是不同的是andThen中两个函数是顺序执行的,即执行原函数,并将原函数的执行结果作为参数(Function)的参数 image.png 示例: image.png

Supplier<T>

Supplier<T>: 表示不需要参数只返回结果的函数。Supplier的意思是生产者/提供者, 常用在提供一些默认值或者已默认的方式提供数据的地方 示例: image.png

Consumer<T>

Consumer<T>: 表示接受单个输入参数且不返回结果的函数。Consumer的意思是消费者,常用在对数据执行操作,并且不需要返回值的地方。Consumer的函数定义如下: image.png 示例: image.png Consumer<T>中也有一个组合函数andThen,表示按顺序消费参数,函数定义如下图: image.png 示例: image.png

Predicate<T>

Predicate<T>: 表示接收单个参数并返回布尔值的函数。Predicate的意思是断言/谓语,常用来表示一些判断,函数定义如下图: image.png Predicate<T>中的组合函数有andornegate,表示与或非逻辑 示例: image.png

柯里化

将一个多参数的函数,转换为一系列单参数函数称为函数柯里化。具体做法为通过将单个参数都转换成函数,参数之间的最终转换成参数和函数之间的联系,例如: image.png

BiFunction<Integer, Integer, Integer>: 表示两个参数有返回值的函数 上图中add函数有两个参数, 柯里化的方式就是将第二个参数和返回值转换成一个新的函数,并作为第一个参数的返回值