共计 10413 个字符,预计需要花费 27 分钟才能阅读完成。
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 值无需再做空值判断,只有没有抛出异样,都会是非空值。 |
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 中生成的异样对象; |
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")));