网站建设方案总结,网站内部链接,wordpress图片全部压缩,0731网站作者简介#xff1a;大家好#xff0c;我是smart哥#xff0c;前中兴通讯、美团架构师#xff0c;现某互联网公司CTO 联系qq#xff1a;184480602#xff0c;加我进群#xff0c;大家一起学习#xff0c;一起进步#xff0c;一起对抗互联网寒冬 咱们今天讨论下函数式接…作者简介大家好我是smart哥前中兴通讯、美团架构师现某互联网公司CTO 联系qq184480602加我进群大家一起学习一起进步一起对抗互联网寒冬 咱们今天讨论下函数式接口
以Predicate为例之前在分析山寨Stream API时我们已经不知不觉使用过函数式接口 // 1.用Lambda表达式实现Predicate接口的抽象方法test()并赋值给函数式接口
PredicateUser ifHigherThan180 user - user.getHeight()180;
// 2.作为参数传入filter(PredicateT ifHigherThan180)filter()会在内部调用test()用来判断
ifHigherThan180.test(user);
总的来说就是把方法ALambda传给方法Bfilter然后在方法B内部调用方法A完成一些操作
public void methodB(method a) {// 操作1// 调用method a// 操作3
}
我们可以理解为Java8支持传递方法之前只能传递基本数据类型和引用类型也可以按原来面向对象的思维将Lambda理解为“特殊的匿名对象”。 为什么函数式接口很重要
有些同学应该会有疑问
函数式接口有什么好讲的我们不是在《Lambda表达式》中学过了吗 不当时只是向大家介绍了函数式接口的定义
有且仅有一个抽象方法的接口不包括默认方法、静态方法以及对Object方法的重写 就我个人学习体会来看函数式接口虽然不难但作为参数类型出现时经常会让初学者感到无所适从甚至不知道该传递什么。比如Stream API中就大量使用了函数式接口接收形参 CompletableFuture中也大量使用了函数式接口 不知道大家是否曾经在调用filter()、supplyAsync()等方法时感到迷茫起码我刚接触Java8时每次调用这些方法都力不从心不知道该传什么进去也不知道Lambda表达式该怎么写。 如果你问我函数式接口难吗我大概率会告诉你它本身很简单。但如果你问我函数式接口重要吗我会告诉你它非常非常重要能否熟练使用Java8的诸多新特性Stream、CompletableFuture等取决于你对函数式接口的熟悉程度。 函数式接口的类型
根据方法的出入参类型不同各种排列组合后其实有很多种类型最常用的有以下4种函数式接口
public class FunctionalInterfaceTest {/*** 给函数式接口赋值的格式* 函数式接口 入参 - 出参/制造出参的语句** param args*/public static void main(String[] args) {FunctionalInterface1 interface1 str - System.out.println(str);FunctionalInterface2 interface2 () - {return abc;};FunctionalInterface3 interface3 str - Integer.parseInt(str);FunctionalInterface4 interface4 str - str.length() 3;}/*** 消费型吃葡萄不吐葡萄皮有入参无返回值* (T t) - {}*/interface FunctionalInterface1 {void method(String str);}/*** 供给型无中生有没有入参却有返回值* () - T t*/interface FunctionalInterface2 {String method();}/*** 映射型转换器把T转成R返回* T t - R r*/interface FunctionalInterface3 {int method(String str);}/*** 特殊的映射型把T转为boolean* T t - boolean*/interface FunctionalInterface4 {boolean method(String str);}
}大家看以上函数式接口是我自定义瞎写的但牢牢把握了住了出入参的特点接口名、方法名不重要代表了4种不同的函数式接口。正因为各自出入参不同导致Lambda表达式的写法也不同。 函数式接口的作用
实际开发中出入参类型的排列组合是有限的所以JDK干脆内置了一部分函数式接口。一般来说我们只需熟练掌握以下4大核心函数式接口即可
消费型接口 ConsumerT void accept(T t)供给型接口 SupplierT T get()函数型接口 FunctionT, R R apply(T t)断定型接口 PredicateT boolean test(T t) 为什么JDK要在Java8这个版本引入函数式接口并且提供这么多内置接口呢 其实这些接口本不是给我们用的而是JDK自己要用。 还记得前几章我编写的山寨Stream API吗为了顺利构造出山寨Stream API我自定义了Predicate接口和Function接口
/*** 新建Predicate接口** param T*/
FunctionalInterface
interface PredicateT {/*** 定义了一个test()方法传入任意对象返回true or false具体判断逻辑由子类实现** param t* return*/boolean test(T t);
}// Function接口 略...
同样的我和Java8一样也把Predicate接口作为形参类型详见myPrint方法
public class MockStream {public static void main(String[] args) {Person bravo new Person(bravo, 18);// 1.匿名对象调用它的test()方法PredicatePerson predicate1 new PredicatePerson() {Overridepublic boolean test(Person person) {return person.getAge() 18;}};myPrint(bravo, predicate1); // false// 2.Lambda表达式调用它的test()方法。// 按照Lambda表达式的标准只要你是个函数式接口那么就可以接收Lambda表达式PredicatePerson predicate2 person - person.getAge() 18;myPrint(bravo, predicate2); // true}public static void myPrint(Person person, PredicatePerson filter) {if (filter.test(person)) {System.out.println(true);} else {System.out.println(false);}}
}
JDK之所以要在Java8内置那么多函数式接口本质原因和我的山寨Stream API一样是为了配合Java8的各种API。单独发布Stream API和CompletableFuture是不现实的它们的方法形参都依赖函数式接口呢。 Java8函数式接口的作用和我们自定义的Predicate接口一样
接收Lambda表达式/方法引用占坑 或者说这两个作用是一体两面的。 所谓占坑之前已经提过了这里再说一下吧。 比如新冠病毒爆发时我们需要对潜在感染者进行检测但检测的手段有很多比如CT和核酸测试。 虽然定义getHealthPerson()时并不确定实际会采用哪种方式进行新冠检测可能是CT也可能是核酸测试具体由子类实现。但不管怎么说有一点是肯定的一定要测试。 此时如果创建一个函数式接口定义boolean test()方法并将getHealthPerson()的形参定为getHealthPerson(ListPerson, PredicatePerson)就能保证未来无论是谁调用这个方法都必须明确检测方法也就是必须传入Predicate接口的实例或者Lambda表达式。
/*** 用Predicate占坑* param uncheckPersonList* param predicate* return*/
public static ListPerson getHealthPerson(ListPerson uncheckPersonList, PredicatePerson predicate) {ArrayListPerson healthyPersonList new ArrayList();for (Person person : uncheckPersonList) {// 占坑反正需要检测但是具体拍CT还是核酸检测具体情况具体分析if (predicate.test(person)) {healthyPersonList.add(person);}}return healthyPersonList;
}
此后如果有其他方法调用getHealthPerson()必须传入具体的检测手段
public static void main(String[] args) throws JsonProcessingException {ArrayListPerson uncheckPersonList new ArrayList();uncheckPersonList.add(new Person(张三, 18));uncheckPersonList.add(new Person(李四, 20));// 传入Lambda表达式getHealthPerson(uncheckPersonList, person - 核酸检测(person));getHealthPerson(uncheckPersonList, person - 胸部CT(person));}
这就是函数式接口的作用。 如何克服面向对象的思维惯性
对于函数式接口上面已经讲得很多了本身很简单没什么好介绍的。这里主要聊一聊初学者应该如何习惯函数式接口传参的方式以及关注点应该放哪里。换句话说帮助初学者克服面向对象的思维惯性学会用函数式编程的思维去使用Java8的诸多特性。 还是以Predicate为例它唯一的抽象方法是
boolean test(T t);
假设有个方法叫getHealthPerson(ListUser users, PredicateUser filter)你知道该给filter传什么吗 很多Java程序员会对这种传参形式感到眩晕因为我们的潜意识一直无法摆脱面向对象的影响。当你看到getHealthPerson(ListUser users, PredicateUser filter)时你的大脑会告诉你Predicate是一个接口所以应该传一个匿名对象给它。然后你的大脑自己也懵了因为它突然发现光看Predicate接口根本不知道该实现什么方法甚至连入参和返回值都不清楚怎么手写Lambda 但之前介绍Python的函数时我们很自然就接受了
# encoding: utf-8# abs是Python的内置函数
result abs(-1)
print(result)# 定义add()方法f显然是一个函数
def add(x, y, f):return f(x) f(y)print(add(-1, -2, abs))
因为相比Python这些原本就支持函数式编程的语言来说形参就是函数不需要用接口做中间层过渡而对于Stream这种面向对象世界中的异类Java程序员还没准备好如何接纳它看到接口形参第一时间想到的还是传匿名对象突然要改成Lambda确实有点棘手因为我们对接口内部的方法及出入参一概不知 如何克服呢两种方式
以面向对象的思维直接new匿名对象靠IDEA优化成Lambda曲线救国减少记忆负担 每个函数式接口就一个方法记住那个方法的出入参看到接口就写对应方法的Lambda需要记忆 这里主要讲讲如何记忆函数式接口的方法。一定要注意函数式接口的方法名字不重要重要的是出入参
消费型接口 ConsumerT T t - void 例x - System.out.println(x)供给型接口 SupplierT () - T t 例() - {return 12;}映射型接口 FunctionT, R T t - R r 例user - user.getAge()T是入参R是出参断定型接口 PredicateT T t - boolean 例user - user.getAge()18 Consumer消费型接口是黑洞只管吃(C)不会往外吐所以只有入参没有返回值。
Supplier供给型接口是泉眼不断往外涌出泉水(S)不需要入参有返回值。
Function映射型接口就是高中数学的函数映射x -- f(x) -- result所以有入参也有返回值。
Predicate断定型接口是特殊的映射函数x -- f(x) -- boolean只会评(P)论true/false。 如果你觉得口诀很生硬就把上面的例子记住把每个函数式接口都和一个例子绑定实际开发时去套用例子即可。 记住了接口对应的出入参Lambda就好办了其实传递的都是
接口声明 入参 - 出参/制造出参的语句 不信你可以重新看看上面给出的例子比如Functionuser - user.getAge()就是把User user映射为Integer ageuser是入参user.getAge()是制造出参的语句。又比如Predicateuser - user.getAge()18user是入参user.getAge()18就是制造出参的语句因为boolean test(T t)需要返回boolean。 所以要习惯函数式接口传参最重要的是记住该接口对应的方法的出入参然后编写Lambda时套用模板
接口声明 入参有无入参 - 出参/制造出参的语句有无出参什么类型 虽然我们还没正式学习Stream API但已经可以试着写写啦
public class FunctionalInterfaceTest {public static void main(String[] args) {ListInteger list new ArrayList(Arrays.asList(1, 2, 3, 4, 5, 6));/*** Predicate特殊映射只断是非 FunctionT t -- f(x) -- boolean* Function映射 FunctionT t -- f(x) -- R r*/list.stream().filter().map().collect(Collectors.toList());// Supplier无源之水(S)却滔滔不绝没有入参却可以get()CompletableFutureObject completableFuture CompletableFuture.supplyAsync();// Consumer黑洞吃(C)葡萄不吐葡萄皮入参拿过来就是一顿操作不返回completableFuture.thenAccept();}}
私底下多写几次形成肌肉记忆就好啦。我们走路时虽然确实有一个规则左右脚要交替向前但我们实际行走时从来不会意识到这个规则否则就走太慢了 函数式接口与类型推断
之前在《Lambda表达式》中我举过一个例子 当接口内只有一个抽象方法时可以使用Lambda表达时给接口赋值
MyInterface interface () - System.out.println(吃东西) 但如果函数式接口内有多个方法抽象方法就无法传递Lambda表达式 此时只能传入匿名对象分别实现两个抽象方法 学完这一章相信大家会有更深刻的理解
public void run()和public void eat()如果只看出入参其实是一样的所以编译时无法自动推断。 由于本章节一直在强调函数式接口传参所以这里再给出一个例子
public class FunctionalInterfaceTest {public static void main(String[] args) {// Ambiguous method calllambdaMethod(() - System.out.println(test));}/*** 方法重载** param param1*/public static void lambdaMethod(Param1 param1) {param1.print();}/*** 方法重载** param param2*/public static void lambdaMethod(Param2 param2) {param2.print();}/*** 函数式接口1*/interface Param1 {void print();}/*** 函数式接口2*/interface Param2 {void print();}} 原因也是一样的Lambda只关注方法的出入参所以在进行类型推断时interface Param1和interface Param2是一样的编译器无法替我们决定实现哪一个接口解决办法是使用匿名对象。 如果说《Lambda表达式》中举的例子是方法级别的推断冲突那么上面的例子讲的就是接口级别的推断冲突但归根结底就一句话Lambda上下文推断只看出入参无论是接口名还是方法名都不重要 所以学习函数式接口最重要的就是记住出入参的格式 回顾ConvertUtil
之前在《实用小算法》中我们封装过一个工具类当时大家可能对于函数式接口并不熟悉现在重新复习一下看看自己能否完全理解这个工具类的设计细节
public class ConvertUtil {private ConvertUtil() {}/*** 将List转为Map** param list 原数据* param keyExtractor Key的抽取规则* param K Key* param V Value* return*/public static K, V MapK, V listToMap(ListV list, FunctionV, K keyExtractor) {if (list null || list.isEmpty()) {return new HashMap();}MapK, V map new HashMap(list.size());for (V element : list) {K key keyExtractor.apply(element);if (key null) {continue;}map.put(key, element);}return map;}/*** 将List转为Map可以指定过滤规则** param list 原数据* param keyExtractor Key的抽取规则* param predicate 过滤规则* param K Key* param V Value* return*/public static K, V MapK, V listToMap(ListV list, FunctionV, K keyExtractor, PredicateV predicate) {if (list null || list.isEmpty()) {return new HashMap();}MapK, V map new HashMap(list.size());for (V element : list) {K key keyExtractor.apply(element);if (key null || !predicate.test(element)) {continue;}map.put(key, element);}return map;}/*** 将List映射为List比如ListPerson personList转为ListString nameList** param originList 原数据* param mapper 映射规则* param T 原数据的元素类型* param R 新数据的元素类型* return*/public static T, R ListR resultToList(ListT originList, FunctionT, R mapper) {if (originList null || originList.isEmpty()) {return new ArrayList();}ListR newList new ArrayList(originList.size());for (T originElement : originList) {R newElement mapper.apply(originElement);if (newElement null) {continue;}newList.add(newElement);}return newList;}/*** 将List映射为List比如ListPerson personList转为ListString nameList* 可以指定过滤规则** param originList 原数据* param mapper 映射规则* param predicate 过滤规则* param T 原数据的元素类型* param R 新数据的元素类型* return*/public static T, R ListR resultToList(ListT originList, FunctionT, R mapper, PredicateT predicate) {if (originList null || originList.isEmpty()) {return new ArrayList();}ListR newList new ArrayList(originList.size());for (T originElement : originList) {R newElement mapper.apply(originElement);if (newElement null || !predicate.test(originElement)) {continue;}newList.add(newElement);}return newList;}// ---------- 以下是测试案例 ----------private static ListPerson list;static {list new ArrayList();list.add(new Person(i, 18, 杭州, 999.9));list.add(new Person(am, 19, 温州, 777.7));list.add(new Person(iron, 21, 杭州, 888.8));list.add(new Person(man, 17, 宁波, 888.8));}public static void main(String[] args) {MapString, Person nameToPersonMap listToMap(list, Person::getName);System.out.println(nameToPersonMap);MapString, Person personGt18 listToMap(list, Person::getName, person - person.getAge() 18);System.out.println(personGt18);}DataAllArgsConstructorNoArgsConstructorstatic class Person {private String name;private Integer age;private String address;private Double salary;}
} 小试牛刀
实际开发中经常会看到这样的写法
public void XxMethod(){// ...ListLong iidList new ArrayList();ListLong eventList new ArrayList();if(items ! null items.size 0) {for(Item item : items){iidList.add(item.getId());eventList.add(item.getEvent());}}// ...
}
这里能用ConvertUtil的resultToList()吗
答案是不能。resultToList()一次只处理一个List而上面一次循环可能处理N个List。所以我们能做的只是抽取循环的操作具体循环里做什么每个List可能不同不好抽取。
有了上面的经验我们完全可以再给ConvertUtil封装个方法
/*** foreach内部判空** param originList 需要遍历的List* param processor 需要执行的操作* param T*/
public static T void foreachIfNonNull(ListT originList, ConsumerT processor) {if (originList null || originList.isEmpty()) {return;}for (T originElement : originList) {if (originElement ! null) {processor.accept(originElement);}}
}
public void XxMethod(){// ...ListLong iidList new ArrayList();ListLong eventList new ArrayList();ConvertUtil.foreachIfNonNull(items, item - {iidList.add(item.getId());eventList.add(item.getEvent());})// ...
}
上面相当于自己实现了foreach。你可能会想为什么不直接用Stream API或者直接List.foreach()原因在于它们都要额外判断非空否则可能引发NPE。 最后留个思考题自己实现groupBy()。
作者简介大家好我是smart哥前中兴通讯、美团架构师现某互联网公司CTO 进群大家一起学习一起进步一起对抗互联网寒冬 文章转载自: http://www.morning.xhsxj.cn.gov.cn.xhsxj.cn http://www.morning.qpqcq.cn.gov.cn.qpqcq.cn http://www.morning.jzlkq.cn.gov.cn.jzlkq.cn http://www.morning.c7512.cn.gov.cn.c7512.cn http://www.morning.bryyb.cn.gov.cn.bryyb.cn http://www.morning.hxbjt.cn.gov.cn.hxbjt.cn http://www.morning.blfll.cn.gov.cn.blfll.cn http://www.morning.jcpq.cn.gov.cn.jcpq.cn http://www.morning.qqklk.cn.gov.cn.qqklk.cn http://www.morning.lmfxq.cn.gov.cn.lmfxq.cn http://www.morning.lggng.cn.gov.cn.lggng.cn http://www.morning.pctql.cn.gov.cn.pctql.cn http://www.morning.nmbbt.cn.gov.cn.nmbbt.cn http://www.morning.hotlads.com.gov.cn.hotlads.com http://www.morning.rqjfm.cn.gov.cn.rqjfm.cn http://www.morning.plznfnh.cn.gov.cn.plznfnh.cn http://www.morning.snbry.cn.gov.cn.snbry.cn http://www.morning.xqxrm.cn.gov.cn.xqxrm.cn http://www.morning.kyhnl.cn.gov.cn.kyhnl.cn http://www.morning.srbmc.cn.gov.cn.srbmc.cn http://www.morning.ywrt.cn.gov.cn.ywrt.cn http://www.morning.dyxlm.cn.gov.cn.dyxlm.cn http://www.morning.smry.cn.gov.cn.smry.cn http://www.morning.kcwkt.cn.gov.cn.kcwkt.cn http://www.morning.zhqfn.cn.gov.cn.zhqfn.cn http://www.morning.cgthq.cn.gov.cn.cgthq.cn http://www.morning.kqnwy.cn.gov.cn.kqnwy.cn http://www.morning.fksyq.cn.gov.cn.fksyq.cn http://www.morning.ptwqf.cn.gov.cn.ptwqf.cn http://www.morning.czrcf.cn.gov.cn.czrcf.cn http://www.morning.jgncd.cn.gov.cn.jgncd.cn http://www.morning.kwrzg.cn.gov.cn.kwrzg.cn http://www.morning.zcncb.cn.gov.cn.zcncb.cn http://www.morning.bnxfj.cn.gov.cn.bnxfj.cn http://www.morning.jfbrt.cn.gov.cn.jfbrt.cn http://www.morning.jxfsm.cn.gov.cn.jxfsm.cn http://www.morning.plxnn.cn.gov.cn.plxnn.cn http://www.morning.ptmch.com.gov.cn.ptmch.com http://www.morning.cbczs.cn.gov.cn.cbczs.cn http://www.morning.rykx.cn.gov.cn.rykx.cn http://www.morning.tbwsl.cn.gov.cn.tbwsl.cn http://www.morning.btmwd.cn.gov.cn.btmwd.cn http://www.morning.pyzt.cn.gov.cn.pyzt.cn http://www.morning.xoaz.cn.gov.cn.xoaz.cn http://www.morning.gwqcr.cn.gov.cn.gwqcr.cn http://www.morning.cfmrb.cn.gov.cn.cfmrb.cn http://www.morning.rlqqy.cn.gov.cn.rlqqy.cn http://www.morning.qkrzn.cn.gov.cn.qkrzn.cn http://www.morning.0small.cn.gov.cn.0small.cn http://www.morning.yrmgh.cn.gov.cn.yrmgh.cn http://www.morning.wjjxr.cn.gov.cn.wjjxr.cn http://www.morning.mtymb.cn.gov.cn.mtymb.cn http://www.morning.tpyrn.cn.gov.cn.tpyrn.cn http://www.morning.ndmh.cn.gov.cn.ndmh.cn http://www.morning.wjlrw.cn.gov.cn.wjlrw.cn http://www.morning.mmplj.cn.gov.cn.mmplj.cn http://www.morning.lgmgn.cn.gov.cn.lgmgn.cn http://www.morning.kkwbw.cn.gov.cn.kkwbw.cn http://www.morning.tqjks.cn.gov.cn.tqjks.cn http://www.morning.dglszn.com.gov.cn.dglszn.com http://www.morning.xzqzd.cn.gov.cn.xzqzd.cn http://www.morning.qqxmj.cn.gov.cn.qqxmj.cn http://www.morning.zqfz.cn.gov.cn.zqfz.cn http://www.morning.bnfrj.cn.gov.cn.bnfrj.cn http://www.morning.qpqcq.cn.gov.cn.qpqcq.cn http://www.morning.tgydf.cn.gov.cn.tgydf.cn http://www.morning.pqppj.cn.gov.cn.pqppj.cn http://www.morning.hcwlq.cn.gov.cn.hcwlq.cn http://www.morning.wsnjn.cn.gov.cn.wsnjn.cn http://www.morning.rfjmy.cn.gov.cn.rfjmy.cn http://www.morning.hrtct.cn.gov.cn.hrtct.cn http://www.morning.drgmr.cn.gov.cn.drgmr.cn http://www.morning.hylbz.cn.gov.cn.hylbz.cn http://www.morning.kwrzg.cn.gov.cn.kwrzg.cn http://www.morning.hxlch.cn.gov.cn.hxlch.cn http://www.morning.rui931.cn.gov.cn.rui931.cn http://www.morning.ejknty.cn.gov.cn.ejknty.cn http://www.morning.tgtwy.cn.gov.cn.tgtwy.cn http://www.morning.xkzr.cn.gov.cn.xkzr.cn http://www.morning.wlgpz.cn.gov.cn.wlgpz.cn