简析JAVA8函数式接口
一,定义
“有且只有一个抽象方法的接口”----函数式接口的定义。
@FunctionalInterface
public interface Ifun{
void test();
}
如上就是一个简单的函数式接口的定义。@FunctionalInterface就表示这是一个函数式接口,你不定义也可以,其实这就是个编译选项,用来强制验证你到底是不是函数式接口。像这样的接口,我们就可以使用Lambda表达式来进行函数式编程,调用方式为()-{},如下:
public class Test {
public static void funTest(Ifun ifun) {
ifun.test();
}
public static void main(String[] args) {
Test.funTest(() -> {
System.out.println("hello world!");
});
}
}
out:
"hello world!"
代码还是比较简单的,现在开发用到最多的场景还是流这一块的操作,替换了以前的比较丑陋的一些遍历操作,你可以说他只是一种语法糖的包装,但是不可否认确实带来代码上的简洁和书写上的流畅。
具体我自己的体会就是:
函数式接口只提供了数据和行为,但是行为的实现放在每个具体的业务场景中。
这个其实有点像匿名内部类的变种的感觉,它只是表现形式上和匿名内部类相似,但是和它又有些不一样,它提供了更加丰富的操作过程。
下面通过JAVA四大内置函数式接口,结合例子来具体了解下这句话。
二,四大内置函数式接口

1. Consumer接口

上面是机翻,虽然有些句子表述不太准确,但是大概的意思我们都能理解,消费接口就是一个接受参数然后进行消费处理的操作,甚至于把异常处理的过程都写了很清楚。下面看个例子:
public class Test {
public static void consumerIt(Consumer<String> cook, Consumer<String> eat) {
cook.andThen(eat).accept("煮饭,吃饭");
}
public static void main(String[] args) {
Test.consumerIt((i) -> {
System.out.println(i.split(",")[0]);
}, (i) -> {
System.out.println(i.split(",")[1]);
});
}
}
out:
煮饭
吃饭
你得先煮饭,才能吃饭,在函数定义中我们就已经把这个顺序给固定了,同时给了入参,所以我们后续只要把对应的每个步骤的具体实现过程进行处理就行了。这边要注意的一点是,这里虽然有先后顺序,但是没有因果关系,不知道这么表述有没有问题,意思就是andThen里after的入参跟之前的入参是一样的,不是说你before里执行处理过的数据再给after,after里的也是原始入参,这一点其实在源码中已经写的很清楚,而且Consumer接口本来也只执行处理,并没有返回,所以这一点一定要弄清楚,不小心有时会弄混。
源码应用
/**
* 检查spring容器里是否有对应的bean,有则进行消费
*
* @param clazz class
* @param consumer 消费
* @param <T> 泛型
*/
private <T> void getBeanThen(Class<T> clazz, Consumer<T> consumer) {
if (this.applicationContext.getBeanNamesForType(clazz, false, false).length > 0) {
consumer.accept(this.applicationContext.getBean(clazz));
}
}
// TODO 注入填充器
this.getBeanThen(MetaObjectHandler.class, globalConfig::setMetaObjectHandler);
// TODO 注入主键生成器
this.getBeanThen(IKeyGenerator.class, i -> globalConfig.getDbConfig().setKeyGenerator(i));
// TODO 注入sql注入器
this.getBeanThen(ISqlInjector.class, globalConfig::setSqlInjector);
// TODO 注入ID生成器
this.getBeanThen(IdentifierGenerator.class, globalConfig::setIdentifierGenerator);
Mybatis-plus这一段查找bean进行消费的处理应用的就是很简单但是经典。
2.Supplier接口

Supplier接口很简单,就一个无参的get方法,用来获取一个泛型对象数据,说白了就是提供一个数据生成。下面看个例子:
public class Test {
public static String supplierGet(Supplier<String> supplier) {
return supplier.get();
}
public static void main(String[] args) {
System.out.println(Test.supplierGet(() -> "hello world!"));
}
}
out:
hello world!
源码应用
public class ClientHttpRequestFactorySupplier implements Supplier<ClientHttpRequestFactory> {
private static final Map<String, String> REQUEST_FACTORY_CANDIDATES;
static {
Map<String, String> candidates = new LinkedHashMap<>();
candidates.put("org.apache.http.client.HttpClient",
"org.springframework.http.client.HttpComponentsClientHttpRequestFactory");
candidates.put("okhttp3.OkHttpClient", "org.springframework.http.client.OkHttp3ClientHttpRequestFactory");
REQUEST_FACTORY_CANDIDATES = Collections.unmodifiableMap(candidates);
}
@Override
public ClientHttpRequestFactory get() {
for (Map.Entry<String, String> candidate : REQUEST_FACTORY_CANDIDATES.entrySet()) {
ClassLoader classLoader = getClass().getClassLoader();
if (ClassUtils.isPresent(candidate.getKey(), classLoader)) {
Class<?> factoryClass = ClassUtils.resolveClassName(candidate.getValue(), classLoader);
return (ClientHttpRequestFactory) BeanUtils.instantiateClass(factoryClass);
}
}
return new SimpleClientHttpRequestFactory();
}
}
springboot中ClientHttpRequestFactory的生成就是实现的Supplier接口,它基于类路径上的可用实现来检测首选候选对象。
3.Function接口


Function接口表示用来接受一个参数并产生结果的函数,同时跟Consumer接口很相似,他也有一个后置函数andThen,同时还有一个前置函数compose。
Function接口乍一看跟Consumer接口很像,但是区别是他是有返回参数的,而Consumer接口没有,他是有因果关系。具体我们看下例子:
public class Test {
public static String funGet(Function<String, String> before, Function<String, String> function, Function<String, String> after) {
return function.compose(before).andThen(after).apply("煮饭,吃饭,洗碗");
}
public static void main(String[] args) {
System.out.println(Test.funGet((t) -> {
System.out.println(t.split(",")[0]);
t = t.substring(t.indexOf(",") + 1);
return t;
}, (t) -> {
System.out.println(t.split(",")[0]);
t = t.substring(t.indexOf(",") + 1);
return t;
}, (t) -> t));
}
}
out:
煮饭
吃饭
洗碗
上面就看出来了,他是before->fun->after这么一个流程,并且每一步的输入都是上一步的输出,这样就很清楚了。
源码应用
@Test
void testWithCallable() {
test(mapper -> mapper.getUserWithCallableAndUnset(new RowBounds(5, 3)), 0);
test(mapper -> mapper.getUserWithCallableAndDefault(new RowBounds(4, 3)), 1);
test(mapper -> mapper.getUserWithCallableAndForwardOnly(new RowBounds(3, 3)), 2);
test(mapper -> mapper.getUserWithCallableAndScrollInsensitive(new RowBounds(2, 2)), 2);
test(mapper -> mapper.getUserWithCallableAndScrollSensitive(new RowBounds(1, 1)), 1);
}
private void test(Function<Mapper, List<User>> usersSupplier, int expectedSize) {
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
Mapper mapper = sqlSession.getMapper(Mapper.class);
List<User> users = usersSupplier.apply(mapper);
Assertions.assertEquals(expectedSize, users.size());
}
}
mybaits里这一段单元测试执行mapper来获得userList就用的很经典,虽然没有用到before和after,但是确实常用的就是apply更多一点,再有就是其实我们现实开发中用到最多的funciton接口应该是map。
类似于valueList.stream().map(ValueText::getValue).collect(Collectors.toSet())这种我们常用的map操作,其实操作的就是一个funciton。
4.Predicate接口


我们先来看下Predicate的字面意思:数理逻辑中表示一个个体的性质和两个或两个以上个体间关系的词。看起来似明非明的,其实很简单就是个true or false。
其实就是用来做表达式验证的,输入一个参数,返回表达式是否成立的结果true or false。 具体我们看下例子:
public class Test {
public static boolean preGet(Predicate<String> predicate) {
return predicate.test("我是帅哥");
}
public static void main(String[] args) {
System.out.println(Test.preGet((t) -> t.contains("帅哥")
));
}
}
out:
true
Predicate接口还是比较容易理解的,其它几个方法:and,negate,or,isEqual光从字面上都能理解,无非就是常用的判断用语,所以就不一一展示了。
源码应用
abstract class IsTestableMethod implements Predicate<Method> {
private final Class<? extends Annotation> annotationType;
private final boolean mustReturnVoid;
IsTestableMethod(Class<? extends Annotation> annotationType, boolean mustReturnVoid) {
this.annotationType = annotationType;
this.mustReturnVoid = mustReturnVoid;
}
@Override
public boolean test(Method candidate) {
// Please do not collapse the following into a single statement.
if (isStatic(candidate)) {
return false;
}
if (isPrivate(candidate)) {
return false;
}
if (isAbstract(candidate)) {
return false;
}
if (returnsVoid(candidate) != this.mustReturnVoid) {
return false;
}
return isAnnotated(candidate, this.annotationType);
}
}
junit中的IsTestableMethod就是比较典型的用法,实现test方法去判断测试方法的类型。其实我们常用的stream操作filter(m -> m.getValue() > 1)中的filter方法用来过滤就是用的Predicate。
三,其它
在java.util.function包下其实不仅仅是这四大接口,其它也有其它好几个它们的变种,无非是把泛型改成具体类型等一系列操作,有兴趣的话,具体可以到对应的路径下去查看。

简析JAVA8函数式接口的更多相关文章
- java代码之美(14)---Java8 函数式接口
Java8 函数式接口 之前写了有关JDK8的Lambda表达式:java代码之美(1)---Java8 Lambda 函数式接口可以理解就是为Lambda服务的,它们组合在一起可以让你的代码看去更加 ...
- java代码(14) --Java8函数式接口
Java8函数式接口 之前有关JDK8的Lambda表达式 Java代码(1)--Java8 Lambda 函数式接口可以理解就是为Lambda服务的,它们组合在一起可以让你的代码看去更加简洁 一.概 ...
- Java8 函数式接口-Functional Interface
目录 函数式接口: JDK 8之前已有的函数式接口: 新定义的函数式接口: 函数式接口中可以额外定义多个Object的public方法一样抽象方法: 声明异常: 静态方法: 默认方法 泛型及继承关系 ...
- Java8函数式接口和Lambda表达式
两者关系: Lambda表达式就是函数式接口(FunctionalInterface)实现的快捷方式,它相当于函数式接口实现的实例,因为在方法中可以使用Object作为参数,所以把Lambda表达式作 ...
- java8 函数式接口——Function/Predict/Supplier/Consumer
Function 我们知道Java8的最大特性就是函数式接口.所有标注了@FunctionalInterface注解的接口都是函数式接口,具体来说,所有标注了该注解的接口都将能用在lambda表达式上 ...
- java8函数式接口(Functional Interface)
介绍 函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口. 函数式接口可以被隐式转换为 lambda 表达式(箭头函数). 函数式接口代 ...
- Java8 函数式接口 @FunctionalInterface以及常用Consumer<T>、Supplier<T>、Function<T, R>、Predicate<T>总结
首先看看什么是Lambda 表达式 Lambda是一个匿名函数,我们可以把Lambda表达式理解为一段可以传递的代码(将代码像数据一样传递):最简单的Lambda表达式可由逗号分隔的参数列表.-> ...
- [二] java8 函数式接口详解 函数接口详解 lambda表达式 匿名函数 方法引用使用含义 函数式接口实例 如何定义函数式接口
函数式接口详细定义 package java.lang; import java.lang.annotation.*; /** * An informative annotation type use ...
- Java8函数式接口/Lambda表达式/接口默认方法/接口静态方法/接口冲突方法重写/lambda表达式指定泛型类型等
一:函数式接口 1.函数式接口的概念就是此接口必须有且只能有一个抽象方法,可以通过@FunctionalInterface来显示规定(类似@Override),但是没有此注解的但是只有一个抽象方法的接 ...
随机推荐
- (数据科学学习手札109)Python+Dash快速web应用开发——静态部件篇(中)
本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...
- Qt+opencv亲自配置教程
了别人的配置,总是无法配置成功,自己慢慢摸索配置成功.我失败的原因是在于自己本机的环境变量和他们不同,特此记下,分享给有相同问题的朋友. 一.需要软件 1.cmake 3.11.3(版本无所谓) 2. ...
- 从头捋了一遍 Java 代理机制,收获颇丰
尽人事,听天命.博主东南大学硕士在读,热爱健身和篮球,乐于分享技术相关的所见所得,关注公众号 @ 飞天小牛肉,第一时间获取文章更新,成长的路上我们一起进步 本文已收录于 「CS-Wiki」Gitee ...
- Docker镜像构建原理解析(不装docker也能构建镜像)
在devops流程里面 构建镜像是一个非常重要的过程,一般构建镜像是写dockerfile文件然后通过docker client来构建的image. docker client 会先检查本地有没有im ...
- [HDU5592] ZYB's Premutation
[HDU5592] ZYB's Premutation 题目大意:一个由\([1,n]\)组成的数列,但不知道具体排列,但给出每个前缀的逆序对数目,让你还原排列 Solution 创造一颗\([1,n ...
- 【Django必备01】——什么是Django框架?有什么优势?模块组成介绍。
01.什么是Django框架? Django是一个开放源代码的Web应用框架,由Python写成.采用了MTV的框架模式.使用这种架构,程序员可以方便.快捷地创建高品质.易维护.数据库驱动的应用程序. ...
- python基础学习之函数进阶【匿名函数、作用域关系、闭包、递归】
匿名函数 lambda的用法: lambda x:x+1 解释,同等于以下函数 def test(x): return x+1 因为没有函数名,所以称为匿名函数 只适用于简易的逻辑,复杂逻辑无法实现 ...
- 策略模式在PHP业务代码的实践
[大话设计模式]-- 策略者模式(Strategy):它定义了算法家族,分别封装起来,让他们之间可以互相替换,此模式让算法的变法,不会影响到使用算法的客户. 策略模式的核心就是屏蔽内部策略算法,内部的 ...
- github个人主页 阿里云域名的绑定
域名解析 我在阿里云上买了一个新域名:gaolu.name,我已经在GitHub Pages上建立了自己的博客:http://gaolu1215.github.io.现在我希望将gaolu.name映 ...
- 【linux】驱动-1-环境准备
目录 前言 1. 开发环境搭建 1.1 环境准备 1.1.1 安装工具 1.1.2 编译内核 1.1.2.1 获取内核源码 1.1.2.2 编译内核 1.2 内核驱动模块编译和加载 1.2.1 hel ...