Java8新引入函数式编程方式,大大的提升了编码效率。本文将对涉及的对象等进行统一的学习及记录。java
首先须要清楚一个概念:函数式接口;它指的是有且只有一个未实现的方法的接口,通常经过FunctionalInterface这个注解来代表某个接口是一个函数式接口。函数式接口是Java支持函数式编程的基础。编程
本文目录:数组
3 函数式编程接口的使用app
3.1 Streamdom
3.2 Optional函数式编程
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函数的实现。
当函数体中只有一个语句时,能够去掉{}进一步简化:
Consumer c = (o) -> System.out.println(o);
然而这还不是最简的,因为此处只是进行打印,调用了System.out中的println静态方法对输入参数直接进行打印,所以能够简化成如下写法:
Consumer c = System.out::println;
它表示的意思就是针对输入的参数将其调用System.out中的静态方法println进行打印。
到这一步就能够感觉到函数式编程的强大能力。
经过最后一段代码,咱们能够简单的理解函数式编程,Consumer接口直接就能够当成一个函数了,这个函数接收一个输入参数,而后针对这个输入进行处理;固然其本质上仍旧是一个对象,但咱们已经省去了诸如老方式中的对象定义过程,直接使用一段代码来给函数式接口对象赋值。
并且最为关键的是,这个函数式对象由于本质上仍旧是一个对象,所以能够作为其它方法的参数或者返回值,能够与原有的代码实现无缝集成!
下面对Java中的几个预先定义的函数式接口及其常用的类进行分析学习。
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"); }
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")); }
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")); }
经过Stream以及Optional两个类,能够进一步利用函数式接口来简化代码。
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对象
这个比较好理解再也不赘述。
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对象是并行的 |
下面就几个比较经常使用的方法举例说明其用法:
用于对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);
元素一对一转换。
它接收一个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);
元素一对多转换:对原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);
方法定义以下:
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);
与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);
关于reduce与collect因为功能较为复杂,在后续将进行单独分析与学习,此处暂不涉及。
用于简化Java中对空值的判断处理,以防止出现各类空指针异常。
Optional其实是对一个变量进行封装,它包含有一个属性value,实际上就是这个变量的值。
它的构造函数都是private类型的,所以要初始化一个Optional的对象没法经过其构造函数进行建立。它提供了一系列的静态方法用于构建Optional对象:
用于建立一个空的Optional对象;其value属性为Null。
如:
Optional o = Optional.empty();
根据传入的值构建一个Optional对象;
传入的值必须是非空值,不然若是传入的值为空值,则会抛出空指针异常。
使用:
o = Optional.of("test");
根据传入值构建一个Optional对象
传入的值能够是空值,若是传入的值是空值,则与empty返回的结果是同样的。
Optional包含如下方法:
方法名 | 说明 |
---|---|
get | 获取Value的值,若是Value值是空值,则会抛出NoSuchElementException异常;所以返回的Value值无需再作空值判断,只要没有抛出异常,都会是非空值。 |
isPresent | Value是否为空值的判断; |
ifPresent | 当Value不为空时,执行传入的Consumer; |
ifPresentOrElse | Value不为空时,执行传入的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对象; |
orElse | Value不为空则返回Value,不然返回传入的值; |
orElseGet | Value不为空则返回Value,不然返回传入的Supplier生成的值; |
orElseThrow | Value不为空则返回Value,不然抛出Supplier中生成的异常对象; |
经常使用的使用场景以下:
如某个函数可能会返回空值,以往的作法:
String s = test(); if (null != s) { System.out.println(s); }
如今的写法就能够是:
Optional<String> s = Optional.ofNullable(test()); s.ifPresent(System.out::println);
乍一看代码复杂度上差很少甚至是略有提高;那为何要这么作呢?
通常状况下,咱们在使用某一个函数返回值时,要作的第一步就是去分析这个函数是否会返回空值;若是没有进行分析或者分析的结果出现误差,致使函数会抛出空值而没有作检测,那么就会相应的抛出空指针异常!
而有了Optional后,在咱们不肯定时就能够不用去作这个检测了,全部的检测Optional对象都帮忙咱们完成,咱们要作的就是按上述方式去处理。
如要判断某个变量为空时使用提供的值,而后再针对这个变量作某种运算;
以往作法:
if (null == s) { s = "test"; } System.out.println(s);
如今的作法:
Optional<String> o = Optional.ofNullable(s); System.out.println(o.orElse("test"));
以往写法:
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")));