Java8新引入函数式编程形式,大大的进步了编码效率。本文将对波及的对象等进行对立的学习及记录。

首先须要分明一个概念:函数式接口;它指的是有且只有一个未实现的办法的接口,个别通过FunctionalInterface这个注解来表明某个接口是一个函数式接口。函数式接口是Java反对函数式编程的根底。

本文目录:

  • 1 Java8函数式编程语法入门
  • 2 Java函数式接口

    • 2.1 Consumer
    • 2.2 Function
    • 2.3 Predicate
  • 3 函数式编程接口的应用

    • 3.1 Stream

      • 3.1.1 Stream对象的创立
      • 3.1.2 Stream对象的应用

        • 3.1.2.1 filter
        • 3.1.2.2 map
        • 3.1.2.3 flatMap
        • 3.1.2.4 takeWhile
        • 3.1.2.5 dropWhile
        • 3.1.2.6 reduce与collect
    • 3.2 Optional

      • 3.2.1 Optional对象创立

        • 3.2.1.1 empty
        • 3.2.1.2 of
        • 3.2.1.3 ofNullable
      • 3.2.2 办法
      • 3.2.3 应用场景

        • 3.2.3.1 判断后果不为空后应用
        • 3.2.3.2 变量为空时提供默认值
        • 3.2.3.3 变量为空时抛出异样,否则应用

1 Java8函数式编程语法入门

Java8中函数式编程语法可能精简代码。
应用Consumer作为示例,它是一个函数式接口,蕴含一个形象办法accept,这个办法只有输出而无输入。
当初咱们要定义一个Consumer对象,传统的形式是这样定义的:

Consumer c = new Consumer() {    @Override    public void accept(Object o) {        System.out.println(o);    }};

而在Java8中,针对函数式编程接口,能够这样定义:

Consumer c = (o) -> {    System.out.println(o);};

下面已阐明,函数式编程接口都只有一个形象办法,因而在采纳这种写法时,编译器会将这段函数编译后当作该形象办法的实现。
如果接口有多个形象办法,编译器就不晓得这段函数应该是实现哪个办法的了。
因而,=前面的函数体咱们就能够看成是accept函数的实现。

  • 输出:->后面的局部,即被()突围的局部。此处只有一个输出参数,实际上输出是能够有多个的,如两个参数时写法:(a, b);当然也能够没有输出,此时间接就能够是()。
  • 函数体:->前面的局部,即被{}突围的局部;能够是一段代码。
  • 输入:函数式编程能够没有返回值,也能够有返回值。如果有返回值时,须要代码段的最初一句通过return的形式返回对应的值。

当函数体中只有一个语句时,能够去掉{}进一步简化:

Consumer c = (o) -> System.out.println(o);

然而这还不是最简的,因为此处只是进行打印,调用了System.out中的println静态方法对输出参数间接进行打印,因而能够简化成以下写法:

Consumer c = System.out::println;

它示意的意思就是针对输出的参数将其调用System.out中的静态方法println进行打印。
到这一步就能够感触到函数式编程的弱小能力。
通过最初一段代码,咱们能够简略的了解函数式编程,Consumer接口间接就能够当成一个函数了,这个函数接管一个输出参数,而后针对这个输出进行解决;当然其本质上仍旧是一个对象,但咱们曾经省去了诸如老形式中的对象定义过程,间接应用一段代码来给函数式接口对象赋值。
而且最为要害的是,这个函数式对象因为实质上仍旧是一个对象,因而能够做为其它办法的参数或者返回值,能够与原有的代码实现无缝集成!

上面对Java中的几个事后定义的函数式接口及其常常应用的类进行剖析学习。

2 Java函数式接口

2.1 Consumer

Consumer是一个函数式编程接口; 顾名思义,Consumer的意思就是生产,即针对某个货色咱们来应用它,因而它蕴含有一个有输出而无输入的accept接口办法;
除accept办法,它还蕴含有andThen这个办法;
其定义如下:

default Consumer<T> andThen(Consumer<? super T> after) {    Objects.requireNonNull(after);    return (T t) -> { accept(t); after.accept(t); };}

可见这个办法就是指定在调用以后Consumer后是否还要调用其它的Consumer;
应用示例:

public static void consumerTest() {    Consumer f = System.out::println;    Consumer f2 = n -> System.out.println(n + "-F2");    //执行完F后再执行F2的Accept办法    f.andThen(f2).accept("test");    //间断执行F的Accept办法    f.andThen(f).andThen(f).andThen(f).accept("test1");}

2.2 Function

Function也是一个函数式编程接口;它代表的含意是“函数”,而函数常常是有输入输出的,因而它含有一个apply办法,蕴含一个输出与一个输入;
除apply办法外,它还有compose与andThen及indentity三个办法,其应用见下述示例;

/** * Function测试 */public static void functionTest() {    Function<Integer, Integer> f = s -> s++;    Function<Integer, Integer> g = s -> s * 2;    /**     * 上面示意在执行F时,先执行G,并且执行F时应用G的输入当作输出。     * 相当于以下代码:     * Integer a = g.apply(1);     * System.out.println(f.apply(a));     */    System.out.println(f.compose(g).apply(1));    /**     * 示意执行F的Apply后应用其返回的值当作输出再执行G的Apply;     * 相当于以下代码     * Integer a = f.apply(1);     * System.out.println(g.apply(a));     */    System.out.println(f.andThen(g).apply(1));    /**     * identity办法会返回一个不进行任何解决的Function,即输入与输出值相等;      */    System.out.println(Function.identity().apply("a"));}

2.3 Predicate

Predicate为函数式接口,predicate的中文意思是“判定”,即判断的意思,判断某个货色是否满足某种条件; 因而它蕴含test办法,依据输出值来做逻辑判断,其后果为True或者False。
它的应用办法示例如下:

/** * Predicate测试 */private static void predicateTest() {    Predicate<String> p = o -> o.equals("test");    Predicate<String> g = o -> o.startsWith("t");    /**     * negate: 用于对原来的Predicate做取反解决;     * 如当调用p.test("test")为True时,调用p.negate().test("test")就会是False;     */    Assert.assertFalse(p.negate().test("test"));    /**     * and: 针对同一输出值,多个Predicate均返回True时返回True,否则返回False;     */    Assert.assertTrue(p.and(g).test("test"));    /**     * or: 针对同一输出值,多个Predicate只有有一个返回True则返回True,否则返回False     */    Assert.assertTrue(p.or(g).test("ta"));}

3 函数式编程接口的应用

通过Stream以及Optional两个类,能够进一步利用函数式接口来简化代码。

3.1 Stream

Stream能够对多个元素进行一系列的操作,也能够反对对某些操作进行并发解决。

3.1.1 Stream对象的创立

Stream对象的创立路径有以下几种

a. 创立空的Stream对象

Stream stream = Stream.empty();

b. 通过汇合类中的stream或者parallelStream办法创立;

List<String> list = Arrays.asList("a", "b", "c", "d");Stream listStream = list.stream();                   //获取串行的Stream对象Stream parallelListStream = list.parallelStream();   //获取并行的Stream对象

c. 通过Stream中的of办法创立:

Stream s = Stream.of("test");Stream s1 = Stream.of("a", "b", "c", "d");

d. 通过Stream中的iterate办法创立:
iterate办法有两个不同参数的办法:

public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f);  public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)

其中第一个办法将会返回一个有限有序值的Stream对象:它的第一个元素是seed,第二个元素是f.apply(seed); 第N个元素是f.apply(n-1个元素的值);生成有限值的办法实际上与Stream的两头办法相似,在遇到停止办法前个别是不真正的执行的。因而有限值的这个办法个别与limit等办法一起应用,来获取前多少个元素。
当然获取前多少个元素也能够应用第二个办法。
第二个办法与第一个办法生成元素的形式相似,不同的是它返回的是一个无限值的Stream;停止条件是由hasNext来判定的。

第二种办法的应用示例如下:

/** * 本示例示意从1开始组装一个序列,第一个是1,第二个是1+1即2,第三个是2+1即3..,间接10时停止; * 也可简化成以下模式: *        Stream.iterate(1, *        n -> n <= 10, *        n -> n+1).forEach(System.out::println); * 写成以下形式是为简化了解 */Stream.iterate(1,        new Predicate<Integer>() {            @Override            public boolean test(Integer integer) {                return integer <= 10;            }        },    new UnaryOperator<Integer>() {        @Override        public Integer apply(Integer integer) {            return integer+1;        }}).forEach(System.out::println);

e. 通过Stream中的generate办法创立
与iterate中创立有限元素的Stream相似,不过它的每个元素与前一元素无关,且生成的是一个无序的队列。也就是说每一个元素都能够随机生成。因而个别用来创立常量的Stream以及随机的Stream等。
示例如下:

/** * 随机生成10个Double元素的Stream并将其打印 */Stream.generate(new Supplier<Double>() {    @Override    public Double get() {        return Math.random();    }}).limit(10).forEach(System.out::println);//上述写法能够简化成以下写法:Stream.generate(() -> Math.random()).limit(10).forEach(System.out::println);

f. 通过Stream中的concat办法连贯两个Stream对象生成新的Stream对象
这个比拟好了解不再赘述。

3.1.2 Stream对象的应用

Stream对象提供多个十分有用的办法,这些办法能够分成两类:
两头操作:将原始的Stream转换成另外一个Stream;如filter返回的是过滤后的Stream。
终端操作:产生的是一个后果或者其它的复合操作;如count或者forEach操作。

其清单如下所示,办法的具体阐明及应用示例见后文。
所有两头操作

办法阐明
sequential返回一个相等的串行的Stream对象,如果原Stream对象曾经是串行就可能会返回原对象
parallel返回一个相等的并行的Stream对象,如果原Stream对象曾经是并行的就会返回原对象
unordered返回一个不关怀程序的Stream对象,如果原对象曾经是这类型的对象就会返回原对象
onClose返回一个相等的Steam对象,同时新的Stream对象在执行Close办法时会调用传入的Runnable对象
close敞开Stream对象
filter元素过滤:对Stream对象按指定的Predicate进行过滤,返回的Stream对象中仅蕴含未被过滤的元素
map元素一对一转换:应用传入的Function对象对Stream中的所有元素进行解决,返回的Stream对象中的元素为原元素解决后的后果
mapToInt元素一对一转换:将原Stream中的应用传入的IntFunction加工后返回一个IntStream对象
flatMap元素一对多转换:对原Stream中的所有元素进行操作,每个元素会有一个或者多个后果,而后将返回的所有元素组合成一个对立的Stream并返回;
distinct去重:返回一个去重后的Stream对象
sorted排序:返回排序后的Stream对象
peek应用传入的Consumer对象对所有元素进行生产后,返回一个新的蕴含所有原来元素的Stream对象
limit获取无限个元素组成新的Stream对象返回
skip摈弃前指定个元素后应用剩下的元素组成新的Stream返回
takeWhile如果Stream是有序的(Ordered),那么返回最长命中序列(合乎传入的Predicate的最长命中序列)组成的Stream;如果是无序的,那么返回的是所有合乎传入的Predicate的元素序列组成的Stream。
dropWhile与takeWhile相同,如果是有序的,返回除最长命中序列外的所有元素组成的Stream;如果是无序的,返回所有未命中的元素组成的Stream。

所有终端操作

办法阐明
iterator返回Stream中所有对象的迭代器;
spliterator返回对所有对象进行的spliterator对象
forEach对所有元素进行迭代解决,无返回值
forEachOrdered按Stream的Encounter所决定的序列进行迭代解决,无返回值
toArray返回所有元素的数组
reduce应用一个初始化的值,与Stream中的元素一一做传入的二合运算后返回最终的值。每与一个元素做运算后的后果,再与下一个元素做运算。它不保障会按序列执行整个过程。
collect依据传入参数做相干汇聚计算
min返回所有元素中最小值的Optional对象;如果Stream中无任何元素,那么返回的Optional对象为Empty
max与Min相同
count所有元素个数
anyMatch只有其中有一个元素满足传入的Predicate时返回True,否则返回False
allMatch所有元素均满足传入的Predicate时返回True,否则False
noneMatch所有元素均不满足传入的Predicate时返回True,否则False
findFirst返回第一个元素的Optioanl对象;如果无元素返回的是空的Optional; 如果Stream是无序的,那么任何元素都可能被返回。
findAny返回任意一个元素的Optional对象,如果无元素返回的是空的Optioanl。
isParallel判断是否以后Stream对象是并行的

上面就几个比拟罕用的办法举例说明其用法:

3.1.2.1 filter

用于对Stream中的元素进行过滤,返回一个过滤后的Stream
其办法定义如下:

Stream<T> filter(Predicate<? super T> predicate);

应用示例:

Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");//查找所有蕴含t的元素并进行打印s.filter(n -> n.contains("t")).forEach(System.out::println);
3.1.2.2 map

元素一对一转换。
它接管一个Funcation参数,用其对Stream中的所有元素进行解决,返回的Stream对象中的元素为Function对原元素解决后的后果
其办法定义如下:

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

示例,假如咱们要将一个String类型的Stream对象中的每个元素增加雷同的后缀.txt,如a变成a.txt,其写法如下:

Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");s.map(n -> n.concat(".txt")).forEach(System.out::println);
3.1.2.3 flatMap

元素一对多转换:对原Stream中的所有元素应用传入的Function进行解决,每个元素通过解决后生成一个多个元素的Stream对象,而后将返回的所有Stream对象中的所有元素组合成一个对立的Stream并返回;
办法定义如下:

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

示例,假如要对一个String类型的Stream进行解决,将每一个元素的拆分成单个字母,并打印:

Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa");s.flatMap(n -> Stream.of(n.split(""))).forEach(System.out::println);
3.1.2.4 takeWhile

办法定义如下:

default Stream<T> takeWhile(Predicate<? super T> predicate)

如果Stream是有序的(Ordered),那么返回最长命中序列(合乎传入的Predicate的最长命中序列)组成的Stream;如果是无序的,那么返回的是所有合乎传入的Predicate的元素序列组成的Stream。
与Filter有点相似,不同的中央就在当Stream是有序时,返回的只是最长命中序列。
如以下示例,通过takeWhile查找”test”, “t1”, “t2”, “teeeee”, “aaaa”, “taaa”这几个元素中蕴含t的最长命中序列:

Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa", "taaa");//以下后果将打印: "test", "t1", "t2", "teeeee",最初的那个taaa不会进行打印 s.takeWhile(n -> n.contains("t")).forEach(System.out::println);
3.1.2.5 dropWhile

与takeWhile相同,如果是有序的,返回除最长命中序列外的所有元素组成的Stream;如果是无序的,返回所有未命中的元素组成的Stream;其定义如下:

default Stream<T> dropWhile(Predicate<? super T> predicate)

如以下示例,通过dropWhile删除”test”, “t1”, “t2”, “teeeee”, “aaaa”, “taaa”这几个元素中蕴含t的最长命中序列:

Stream<String> s = Stream.of("test", "t1", "t2", "teeeee", "aaaa", "taaa");//以下后果将打印:"aaaa", "taaa" s.dropWhile(n -> n.contains("t")).forEach(System.out::println);
3.1.2.6 reduce与collect

对于reduce与collect因为性能较为简单,在后续将进行独自剖析与学习,此处暂不波及。

3.2 Optional

用于简化Java中对空值的判断解决,以防止出现各种空指针异样。
Optional实际上是对一个变量进行封装,它蕴含有一个属性value,实际上就是这个变量的值。

3.2.1 Optional对象创立

它的构造函数都是private类型的,因而要初始化一个Optional的对象无奈通过其构造函数进行创立。它提供了一系列的静态方法用于构建Optional对象:

3.2.1.1 empty

用于创立一个空的Optional对象;其value属性为Null。
如:

Optional o = Optional.empty();
3.2.1.2 of

依据传入的值构建一个Optional对象;
传入的值必须是非空值,否则如果传入的值为空值,则会抛出空指针异样。
应用:

o = Optional.of("test");
3.2.1.3 ofNullable

依据传入值构建一个Optional对象
传入的值能够是空值,如果传入的值是空值,则与empty返回的后果是一样的。

3.2.2 办法

Optional蕴含以下办法:

办法名阐明
get获取Value的值,如果Value值是空值,则会抛出NoSuchElementException异样;因而返回的Value值无需再做空值判断,只有没有抛出异样,都会是非空值。
isPresentValue是否为空值的判断;
ifPresent当Value不为空时,执行传入的Consumer;
ifPresentOrElseValue不为空时,执行传入的Consumer;否则执行传入的Runnable对象;
filter当Value为空或者传入的Predicate对象调用test(value)返回False时,返回Empty对象;否则返回以后的Optional对象
map一对一转换:当Value为空时返回Empty对象,否则返回传入的Function执行apply(value)后的后果组装的Optional对象;
flatMap一对多转换:当Value为空时返回Empty对象,否则传入的Function执行apply(value)后返回的后果(其返回后果间接是Optional对象)
or如果Value不为空,则返回以后的Optional对象;否则,返回传入的Supplier生成的Optional对象;
stream如果Value为空,返回Stream对象的Empty值;否则返回Stream.of(value)的Stream对象;
orElseValue不为空则返回Value,否则返回传入的值;
orElseGetValue不为空则返回Value,否则返回传入的Supplier生成的值;
orElseThrowValue不为空则返回Value,否则抛出Supplier中生成的异样对象;

3.2.3 应用场景

罕用的应用场景如下:

3.2.3.1 判断后果不为空后应用

如某个函数可能会返回空值,以往的做法:

String s = test();if (null != s) {    System.out.println(s);}

当初的写法就能够是:

Optional<String> s = Optional.ofNullable(test());s.ifPresent(System.out::println);

乍一看代码复杂度上差不多甚至是略有晋升;那为什么要这么做呢?
个别状况下,咱们在应用某一个函数返回值时,要做的第一步就是去剖析这个函数是否会返回空值;如果没有进行剖析或者剖析的后果呈现偏差,导致函数会抛出空值而没有做检测,那么就会相应的抛出空指针异样!
而有了Optional后,在咱们不确定时就能够不必去做这个检测了,所有的检测Optional对象都帮忙咱们实现,咱们要做的就是按上述形式去解决。

3.2.3.2 变量为空时提供默认值

如要判断某个变量为空时应用提供的值,而后再针对这个变量做某种运算;
以往做法:

if (null == s) {    s = "test";}System.out.println(s);

当初的做法:

Optional<String> o = Optional.ofNullable(s);System.out.println(o.orElse("test"));
3.2.3.3 变量为空时抛出异样,否则应用

以往写法:

if (null == s) {    throw new Exception("test");}System.out.println(s);

当初写法:

Optional<String> o = Optional.ofNullable(s);System.out.println(o.orElseThrow(()->new Exception("test")));