一天一个编程小知识之Java中的函数式编程
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数。
为什么java中可以使用函数式编程
Java本身是面向对象的程序设计语言,要想在Java中使用函数式编程的特性,首先需要支持函数式编程。 编程语言中需要有结构来表示函数是支持函数式编程的一大特征 。在Java中使用接口来表示函数,Python中有map和reduce等。 在java中函数的定义
函数式编程可以提高我们的开发效率(什么是函数式编程),那么在Java中怎么定义和使用这样一个特性呢?
上面说明了Java中是通过接口定义 函数 的,所以如果接口中添加了 @FunctionalInterface 注解,则这个接口是可以用做函数式编程的。
当然并不是所有接口都可以添加这个注解的, @FunctionalInterface 注解有几个限制条件,分别是: 接口有且仅有一个抽象方法 允许定义静态方法 允许定义默认方法
直到Java8,Java才提供了内置标准API来表示函数,也就是 java.util.function 包。 Function 表示接受一个参数的函数,输入类型为 T ,输出类型为 R 。
下面以 java.util.function 包下的 Function 接口的源码为例: @FunctionalInterface public interface Function { // 有且仅有一个抽象方法 R apply(T t); // 默认方法1 default Function compose(Function<? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } // 默认方法2 default Function andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); return (T t) -> after.apply(apply(t)); } // 静态方法 static Function identity() { return t -> t; } }
除了 Function ,Java 标准库还提供了几种特殊类型的函数: Consumer:接受一个输入,没有输出。抽象方法为 void accept(T t)。 Supplier:没有输入,一个输出。抽象方法为 T get()。 Predicate:接受一个输入,输出为 boolean 类型。抽象方法为 boolean test(T t)。 UnaryOperator:接受一个输入,输出的类型与输入相同,相当于 Function。 BinaryOperator:接受两个类型相同的输入,输出的类型与输入相同,相当于 BiFunction。 BiPredicate:接受两个输入,输出为 boolean 类型。抽象方法为 boolean test(T t, U u)。 另外如果一个接口满足上面的`@FunctionalInterface`注解的要求可以不需要显示的添加`@FunctionalInterface`注解。 不过通常还是主动添加`@FunctionalInterface`注解,添加注解后如果在当前接口再添加抽象方法时,编辑器会提示不允许添加。如何自定义函数接口
根据 @FunctionalInterface 注解的要求可以实现自定义函数接口。 // 自定义函数接口 @FunctionalInterface public interface CustomAddFunc { R add(T t); }在Java中函数的使用
因为函数式编程的特点是允许把函数本身作为参数传入另一个函数,所以方法的参数含有上面Java标准库或者自定义的函数类型参数时,就是定义了一个函数式编程的方法了。
带函数参数的方法定义
下面以 java.util.stream 包下的 Stream 接口部分源码为例: // Stream接口部分源码 public interface Stream extends BaseStream> { Stream filter(Predicate<? super T> predicate); Stream map(Function<? super T, ? extends R> mapper); }
filter 和 map 方法都有一个Java标准库中特殊类型的函数参数。 `Function`接受一个参数的函数,输入类型为 T,输出类型为 R。 `Predicate`接受一个输入,输出为 boolean 类型。
带函数参数的方法使用 // 通过匿名内部类的方式实现函数接口 int sum = Arrays.stream(new int[]{1, 2, 3}).map(new IntUnaryOperator() { @Override public int applyAsInt(int operand) { return operand + 1; } }).sum(); System.out.println(sum); // 输出 9
优雅的使用带函数参数的方法
Java8提供了lambda表达式,是创建匿名内部类的语法糖(syntax sugar)。所以上面的使用方式可以优化成更短的代码 int l = Arrays.stream(new int[]{1, 2, 3}).map(v -> v + 1).sum(); System.out.println(l);
结合lambda表达式使用函数式编程可以很大程度地减少不必要的代码,达到提高开发效率的目的,而且在表达上也更清晰。