导读 | 函数式编程,这个词语由两个名词构成,函数,编程。函数,其实单独抽离出来这个词语,并不陌生,那二者组合后的到底是什么呢,下面这篇文章主要给大家介绍关于Java函数式编程。 |
Java8所有的新特性基本基于函数式编程的思想,函数式编程的带来,给Java注入了新鲜的活力。
下面来近距离观察一下函数式编程的几个特点:
•函数可以作为变量、参数、返回值和数据类型。
•基于表达式来替代方法的调用
•函数无状态,可以并发和独立使用
•函数无副作用,不会修改外部的变量
•函数结果确定性;同样的输入,必然会有同样的结果。
下面jdk1.8里面对函数式编程的定义。只是一个 FunctionalInterface 接口。特别的简单。
1 @Documented 2 @Retention(RetentionPolicy.RUNTIME) 3 @Target(ElementType.TYPE) 4 public @interface FunctionalInterface {}
这个函数式接口有几点以下的限制:
•唯一的抽象方法,有且仅有一个 (即所有的函数式接口,有且只能有一个抽象方法)
•加上标注,则会触发JavaCompiler的检查。对于符合函数接口的接口,加不加都无关紧要,但是加上则会提供一层编译检查的保障。如果不符合,则会报错。
•不能被覆盖之后,再声明为抽象方法,则不算抽象方法。例如接口实现了Object中的方法。
•可用于lambda类型的使用方式
Stream的操作是建立在函数式接口的组合之上的。Java8中新增的函数式接口都在java.util.function包下。这些函数式接口可以有多种分类方式。
Function是从T到R的一元映射函数。将参数T传递给一个函数,返回R。即R = Function(T)
Function最常用的应该是 Stream map(Function< ? super T, ? extends R> mapper);
比如List person里面有age,name…. 我传入age,他就会返回age的集合给我。
@FunctionalInterface public interface Function { R apply(T t); default Function compose(Function< ? super V, ? extends T> before) { Objects.requireNonNull(before); return (V v) -> apply(before.apply(v)); } 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; } }
Predicate是一个谓词函数,主要作为一个谓词演算推导真假值存在,返回布尔值的函数。Predicate等价于一个Function的boolean型返回值的子集。
predicate最常用的莫过于 Stream filter(Predicate< ? super T> predicate);
比如我要过滤年龄 > 18 的人,我传入age,判断是否为true。为true则保留,false丢弃。
@FunctionalInterface public interface Predicate { boolean test(T t); default Predicate and(Predicate< ? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } default Predicate negate() { return (t) -> !test(t); } default Predicate or(Predicate< ? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) || other.test(t); } static Predicate isEqual(Object targetRef) { return (null == targetRef) ? Objects::isNull : object -> targetRef.equals(object); } }
Consumer是从T到void的一元函数,接受一个入参但不返回任何结果的操作。
Consumer最常用的肯定是 default void forEach(Consumer< ? super T> action) {}
这是一段forEach循环的代码,传入实现的方法,并不返回任何值。只是循环。
@FunctionalInterface public interface Consumer { void accept(T t); default Consumer andThen(Consumer< ? super T> after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
Lambda 的基本结构为 (arguments) -> body,有如下几种情况:
•参数类型可推导时,不需要指定类型,如 (a) -> System.out.println(a)
•当只有一个参数且类型可推导时,不强制写 (), 如 a -> System.out.println(a)
•参数指定类型时,必须有括号,如 (int a) -> System.out.println(a)
•参数可以为空,如 () -> System.out.println(“hello”)
•body 需要用 {} 包含语句,当只有一条语句时 {} 可省略
比如如下代码:
List list = new ArrayList<>(); list.stream().filter((x) -> x >= 18) Stream filter(Predicate< ? super T> predicate); @FunctionalInterface public interface Predicate { boolean test(T t); }
比如List里面存个个人的年龄,现在筛选出年龄大于等于18的人。
此时我们就可以用 list.stream().filter((x) -> x >= 18) 这就是一个典型的lambda表达式
(x) -> x >= 18 传给 Predicate 函数式接口。
原理其实是:
JVM帮我们动态生成了一个内部类,然后这个内部类实现了 Predicate 这个函数式接口。
重写了里面的test方法。生成的类似如下:
static final class Main$$Lambda$1 implements Predicate { private Main$$Lambda$1() { } @Override public boolean test(Integer x) { return x >= 18; } }
public class Main { public static void main(String[] args) { List list = new ArrayList<>(); list.add(40); list.add(50); list.add(20); list.add(30); List collect = list.stream().filter(x -> x >= 30) .map((x) -> x + 10).sorted((x, y) -> -x.compareTo(y)) .collect(Collectors.toList()); System.out.println(collect); } }
这个一段很典型的Lambda + Stream的用法。
•list.stream()获取list的stream的流
•filter筛选出年龄大于30的人 (里面是一个Predicate接口,返回真假)
•map做一个function映射
•sort排序,里面是compartor
Lambda 表达式可以减少很多代码,能提高生产力。但也要理解其原理。比如3.3中的代码,为什么filter里面是断言表达式,map里面是function表达式。
这都要从lambda的原理入手,也就是JVM动态生成一个内部类,并继承其中的抽象方法。
本次主要介绍了Java函数式编程的原理以及应用,主要从Stream和lambda入手。通过一些简单的概念,以及代码,更好的理解Java的函数式编程。
掌握Java的函数式编程,对平时我们开发代码,看其他人的代码,都有很大的帮助。
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/121252.html