其他

Java Lambda 方法引用总结——烧脑吃透

2017-06-14 polly 开源中国


Lambda 是 Java 8 的新特性,基本使用比较容易理解,但有一个环节遇到了坎儿,那就是方法引用,尤其是类的实例方法引用,烧脑之后总结一下。


在需要函数参数的方法中,我们可以把另一个同类型的方法直接传入,这称为方法引用的绑定。类似于C语言中的函数指针。


lambda表达式可以替代方法引用;或者说方法引用是lambda的一种特例,方法引用不可以控制传递参数。


构造器引用


需要有无参的构造器。


静态方法引用


so easy,只要成员方法的参数列表和FI需要的参数一致就可以。


成员方法引用


so easy,只要成员方法的参数列表和FI需要的参数一致就可以。


类的任意对象的实例方法引用(很怪异)


前方高能,请关掉耳机的音乐,认真思考,小心行事。


传统的java开发中,是不允许使用类名去调用成员方法的,这是一个基本原则,那么这里的这种写法就有点不太容易理解了。还是用实例说明:


用到的内部类:



测试代码:



小结

小结一下:



烧脑分析类的实例方法省略了哪个参数


前面的例子,FI的两个参数是同一个类型,如果类型不同呢?省略了哪个参数呢?


是按照位置省略了第一个,亦或者是省略了最后一个?


还是按照类型自动去对应,而不关心第几个呢?


这个时候,我们能想到的办法可能是去看源码,但是一般看代码没有个把礼拜甚至更长,毛都看不出来。我们还是用一个例子来分析一下吧。


定义一个FI接口:



编写两个用于参数的类:


TestBean1.java



TestBean2.java



二者区别不大。


编写测试类:



测试方法中,除了标记OK的行正确,其他都报错。


分析:


首先我们要明确FI需要的参数列表是:


(TestBean1,TestBean2)


1、我们先看①行,我们传入的"::"前导的类是TestBean1,而expect1方法匹配的是TestBean1类型的入参bean1,也就是说省略了TestBean2类型的参数bean2,FI中的最后一个参数。即便我们使用类TestBean1去new一个对象,也找不到TestBean2,因此这个错误。


2、我们先看②行,我们传入的"::"前导的类是TestBean1,而expect2方法匹配的是TestBean2类型的入参bean2,也就是说省略了TestBean1类型的参数bean1,那么lambda就可以使用"::"前导的TestBean1构建一个对象,作为第一个参数,从而匹配FI的接口方法。ok。


3、我们先看③行,我们传入的"::"前导的类是TestBean2,而expect1方法匹配的是TestBean1类型的入参bean1,也就是说省略了TestBean2类型的参数bean2,FI的最后一个参数。按照第二步的分析,我们用"::"前导的类TestBean2去new一个对象,应该可以凑足两个参数。实际测试会发现这不灵。这就证明了只能省略第一个参数,而且,用"::"前导的类也必须是第一个参数的类型。


4、同第一步类似,第④行代码,找不到TestBean1的参数,有错误可以理解。


5、至于⑤~⑧,只是替换了外层的test1的主体,没有任何区别。这证明了,lambda的匹配与外层是什么鬼没有任何关系,它只关心外层需要的FI的参数列表。


6、请不要看下一步,在这里停下来冷静的思考一下,如果我们把TestInterface中FI方法的参数位置换一下,即public void anyStringAsName(TestBean2 cat,TestBean1 dog);,结果应该是哪两行正确呢?认真思考一下,实在想不明白跑一下测试用例,也许对理解更有帮助。


7、如果想明白了用这个思路验证一下:参照参数列表(TestBean2,TestBean1),可以确定只可以省略第一个参数即TestBean2,那么"::"前导类必须是TestBean2,用于自动创建对象;而未省略的参数是TestBean1,那么方法名为expect1,结果为xxx(TestBean2::expect1),即③和⑦,你答对了吗?



推荐阅读

大公司利用开源作品申请专利,谁伤了开源软件作者的心?

Web 开发人员必备的安全检查列表

Kotlin 和 Java EE 系列之 —— 使用插件愉快

NGINX 开发指南(Part 3)

倾力推荐,学习 Kotlin 的 20 个实用资源

“放码过来”邀您亮“项”,一不小心就火了!

点击“阅读原文”查看更多精彩内容

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存