二函数式接口全面剖析
Posted Java码农社区
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二函数式接口全面剖析相关的知识,希望对你有一定的参考价值。
函数式接口全面剖析
一、概述
1、是什么
函数式接口是一个被@FunctionalInterface注解所修饰且接口内只有一个抽象方法。严格意义上讲接口内只有一个抽象方法就是一个函数式接口,而注解只是强制声明一下,带上注解就是必须只能有一个抽象方法,若不符合则编译报错。起到一个限制的作用。
2、有毛用
配合lambda表达式和stream,简直神器。
3、答疑
上一篇我用Runnable和Comparator两个接口做了举例,我还留下个疑问:为什么自己写的接口不行,这两个接口却可以完美结合lambda?看源码可知,这两个类完全符合函数式接口的特征(被@FunctionalInterface修饰且只有一个抽象方法,若没被注解修饰,但是只有一个抽象方法,这样也是可以的)。
二、自定义函数式接口
1、函数式接口
/**
* 有且只有一个抽象方法才可声明函数式接口
* @FunctionalInterface注解只是起到一个限制的作用,加上注解后若不符合函数式接口规范会编译报错。
*/
@FunctionalInterface
public interface AppleFilter {
boolean filter(Apple apple);
}
2、实体类
@Data
@AllArgsConstructor
public class Apple {
/** 颜色 */
private String color;
/** 重量 */
private long weight;
}
3、需求
找到颜色为红色的苹果。
4、码前分析
首先我们自定义的函数式接口是一个有一参且有返回格式的。所以lambda可写成如下:(Apple apple) -> {逻辑处理}
因为需求是找到红色的,所以代码体仅一行,可去掉{}apple -> "red".equals(apple.getColor);
其中apple为函数式接口的参数,省略了类型和(),第一篇讲过,一参可省略(),不知道的回去再看一遍。
4、开始编码
public static void main(String[] args) {
// 初始化苹果集合
List<Apple> list = Arrays.asList(new Apple("green", 10), new Apple("red", 20), new Apple("green", 30));
// 最终的结果
List<Apple> result = new ArrayList<>();
// 声明我们自己的函数式接口的规则,若不用lambda则是如下这个丑样子:
/*
* AppleFilter filter = new AppleFilter() {
* @Override
* public boolean filter(Apple apple) {
* return "red".equals(apple.getColor());
* }
* };
*
* 而lambda简单一句话。param -> body
* 箭头左侧apple代表filter接口的入参,是Apple类型的,这里省略了类型,写全的话是Apple apple。
* 而箭头右侧则是接口的规则,资深的人一眼就发现了,这是策略模式的一种写法。的确如此。
*/
AppleFilter filter = apple -> "red".equals(apple.getColor());
for (Apple apple : list) {
// filter.filter(apple)则是验证apple是否是上述规则(红色),因为filter返回的boolean
if (filter.filter(apple)) {
result.add(apple);
}
}
System.out.println(result);
}
看到这里大家可能还会觉得lambda还是没啥用。就是匿名内部类创建的时候容易了,一句话代替了四五句。不错,目前为止能悟到这个就行了。
三、Java8内置函数式接口
1、内置
2、核心接口
函数式接口 | 参数类型 | 返回类型 | 描述 |
---|---|---|---|
Predicate<T>断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean的值。boolean test(T t); |
Consumer<T>消费型接口 | T | void | 对类型为T的对象进行操作void accept(T t); |
Supplier<T>供给型接口 | 无 | T | 返回类型为T的对象。T get(); |
Function<T,R>函数型接口,最强大的一个 | T | R | 对类型为T的对象进行操作,并返回结果,结果是R类型的对象。R apply(T t); |
3、核心接口的扩展
上面四大核心接口都有N个扩展(可以假装看成是具体的实现。)具体有如下这几类:拿Function函数式接口举例
// 多参数的
java.util.function.BiFunction<T, U, R> {}
// 具体类型的:double
java.util.function.DoubleFunction<R> {}
// 具体类型的:int
java.util.function.IntFunction<R> {}
// 具体类型的:long
java.util.function.LongFunction<R> {}
其他三大核心接口也拥有如上那些具体的函数式接口,很简单的,可以自己去看源码。都在java.util.function
包下。
四、实战演示
1、Predicate断定型接口
1.1、需求
找到颜色为绿色的苹果
1.2、实现
private static List<Apple> findPredicate(List<Apple> list, Predicate<Apple> predicate) {
List<Apple> result = new ArrayList<>();
for (Apple apple : list) {
// 利用Predicate的test方法进行断定,具体业务需要看调用方,是个策略模式
if (predicate.test(apple)) {
result.add(apple);
}
}
return result;
}
public static void main(String[] args) {
List<Apple> list = Arrays.asList(new Apple("green", 10), new Apple("red", 20), new Apple("green", 30));
/*
* 第二个参数是Predicate的test(Apple)方法:
* 由于只有一个参数,所以可以省略小括号
*/
List<Apple> appleList = findPredicate(list, apple -> apple.getColor().equals("green"));
System.out.println(appleList);
}
1.3、LongPredicate
1.3.1、需求
找到重量为30的苹果
1.3.2、实现
private static List<Apple> findLongPredicate(List<Apple> list, LongPredicate predicate) {
List<Apple> result = new ArrayList<>();
for (Apple apple : list) {
// 利用Predicate的test方法进行断定,具体业务需要看调用方,是个策略模式
if (predicate.test(apple.getWeight())) {
result.add(apple);
}
}
return result;
}
public static void main(String[] args) {
List<Apple> list = Arrays.asList(new Apple("green", 10), new Apple("red", 20), new Apple("green", 30));
List<Apple> appleList = findLongPredicate(list, weight -> weight == 30);
System.out.println(appleList);
}
1.4、BiPredicate
1.4.1、需求
找到颜色为绿色且重量为30的苹果
1.4.2、实现
private static List<Apple> findBiPredicate(List<Apple> list, BiPredicate<String, Long> predicate) {
List<Apple> result = new ArrayList<>();
for (Apple apple : list) {
// 利用Predicate的test方法进行断定,具体业务需要看调用方,是个策略模式
if (predicate.test(apple.getColor(), apple.getWeight())) {
result.add(apple);
}
}
return result;
}
public static void main(String[] args) {
List<Apple> list = Arrays.asList(new Apple("green", 10), new Apple("red", 20), new Apple("green", 30));
/*
* 由于只有一个参数,所以不可以省略小括号
*/
List<Apple> appleList = findBiPredicate(list, (color, weight) -> color.equals("green") && weight == 30);
System.out.println(appleList);
}
2、Consumer消费型接口
2.1、需求
利用Consumer写一个公用的apple处理方法(策略模式)。
2.2、实现
private static void findConsumer(List<Apple> list, Consumer<Apple> consumer) {
for (Apple apple : list) {
consumer.accept(apple);
}
}
public static void main(String[] args) {
List<Apple> list = Arrays.asList(new Apple("green", 10), new Apple("red", 20), new Apple("green", 30));
findConsumer(list, apple -> System.out.println(apple));
}
3、Supplier供给型接口
3.1、需求
创建一个apple对象并返回
3.2、实现
private static Apple findSupplier(Supplier<Apple> supplier) {
return supplier.get();
}
public static void main(String[] args) {
/*
* T get();接口无参有返回。
* 所以:
* () -> ...
*/
Apple apple = findSupplier(() -> new Apple("red", 111L));
System.out.println(apple);
}
4、Function函数型接口
4.1、需求
创建一个apple对象并返回对象的toString()
4.2、实现
private static String findFunction(Apple apple, Function<Apple, String> function) {
return function.apply(apple);
}
public static void main(String[] args) {
String s = findFunction(new Apple("red", 1234L), apple -> apple.toString());
System.out.println(s);
}
5、补充
认认真真看demo,自己动手!!!其他的比如BiFunction,LongSupplier等等都自己写吧,举一反三足够了!写到这你会发现比以前更复杂,还不如不用!没毛病,但是这只是开始,我写大量的demo只是为了让你明白有这几种函数式接口的存在,到底什么是函数式接口,到底怎么用。你把这块搞懂了,以后stream的时候你会很顺畅,而且会发现功能无比强大。
五、方法推导
1、是什么
当要传递给lambda体的操作,已经有了实现好的方法可以使用方法进行引用。方法引用:使用操作符“::”,将方法名和对象或类的名字分隔开来。
2、四种方式
对象::实例方法
类::静态方法
类::实例方法
类::new
3、实战
3.1、对象::实例方法
public static void main(String[] args) {
Apple apple = new Apple("black", 123L);
/**
* ::隔开,左侧是对象,右侧是对象的实例方法
*/
Supplier<String> supplier = apple::getColor;
System.out.println(supplier.get());
// 再比如
PrintStream out = System.out;
Consumer<String> consumer = out::println;
consumer.accept("hello");
}
3.2、类::静态方法
public static void main(String[] args) {
// 输入两个int,返回int.
BiFunction<Integer, Integer, Integer> comparator = Math::max;
// 返回2
System.out.println(comparator.apply(1,2));
}
3.3、类::实例方法
public static void main(String[] args) {
BiFunction<String, Object, Boolean> function = String::equals;
System.out.println(function.apply("1", "1"));
}
3.4、类::new
public static void main(String[] args) {
// 无参的 等于new String()
Consumer<String> consumer = String::new;
// 有参的 等于new Apple("black", 111L);
BiFunction<String, Long, Apple> appleBiFunction = Apple::new;
Apple black = appleBiFunction.apply("black", 111L);
System.out.println(black);
}
六、总结
目前为止你应该学会如下:
箭头函数怎么用
函数式接口常用的几种
方法推导
没觉得好,没关系!后面会觉得好。
七、再次理解
案例:
private static List<Apple> findPredicate(List<Apple> list, Predicate<Apple> predicate) {
List<Apple> result = new ArrayList<>();
for (Apple apple : list) {
// 利用Predicate的test方法进行断定,具体业务需要看调用方,是个策略模式
if (predicate.test(apple)) {
result.add(apple);
}
}
return result;
}
public static void main(String[] args) {
List<Apple> list = Arrays.asList(new Apple("green", 10), new Apple("red", 20), new Apple("green", 30));
/*
* 第二个参数是Predicate的test(Apple)方法:
* 由于只有一个参数,所以可以省略小括号
*/
List<Apple> appleList = findPredicate(list, apple -> apple.getColor().equals("green"));
System.out.println(appleList);
}
分析:findPredicate()有两个参数,第一个list没毛病,第二个他要一个Predicate接口的引用,可是我们却传进去一段代码,这是什么鬼?看如下:
if (predicate.test(apple)) {
}
我们利用类进行test(apple),换成我们传进去的代码就是:
if (apple.getColor().equals("green")) {
}
为什么呢?因为test参数apple就是我们当前这个apple,所以最终也就长这个样子。
八、广告
https://gitee.com/geekerdream/
以上是关于二函数式接口全面剖析的主要内容,如果未能解决你的问题,请参考以下文章
[二] java8 函数式接口详解 函数接口详解 lambda表达式 匿名函数 方法引用使用含义 函数式接口实例 如何定义函数式接口