什么是高阶函数
一句话解释的话,就是函数可以作为参数以变量的方式持有,引用,构造和使用
一些文章我随手搜的,大家可以看看,它本身的概念并不复杂。
高阶函数-廖雪峰
高阶函数-wiki
其实现在主流语言差不多都支持了,之前的java7利用接口其实也算支持,c#的委托,js更不用说,写了回调就算。
所以我这里主要是谈谈他对编程思维的影响。
基本讲解
让我们先从kotlin的一个官方库函数的实现开始讲起,这个函数只有一行代码
/**
* Calls the specified function [block] with `this` value as its receiver and returns `this` value.
*/
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T { block(); return this }
apply是接收一个针对T类型的扩展方法的lambda形式(Ps:kt可以将扩展方法以lambda传递,这是它相比C#的一个重要改进)
后面的block(),其实是this.block()的简写(Ps:kt在扩展lambda中,可以省略this这个调用上下文)
表意为,以T作为this,调用一下针对T的(lambda形式)扩展函数,这个lambda可以对T做一些事情,做完了这个函数仍然是返回T.
这个解释可能不太直观,我们继续上代码:(Ps:kt实例化对象时候,调用构造方法即为实例化,不需要使用new关键字)
Ps:这个示例,功能类似于C#的实例化对象时候的属性构造,但意思是完全不同的,后面是一个lambda语句,是可以定义变量,执行语句的。
我们把这个场景分析一下:new一个实例出来,并给他赋一些初始值,然后调用它做一些事情
前两者其实是紧密联系在一起的"原子性逻辑",如果是java,你不得不把这个场景分成三段:
1 new 2 赋值 3 使用
如果是java,我们可能经常见到
1.MyDto dto = new MyDto() ; 紧跟着就是二三十行的赋值代码,需要的值有时候可能还需要通过计算得到,重构吧,又得传一大推参数。
但如果是kotlin使用了apply,那么就成了这样
1 (new, 赋值) 2 使用
注意我的折叠,我一共折叠了十多行的赋值代码,这样阅读起来,new和赋值就是一个整体,
同时如果我在apply中定义的一些临时变量,会被锁在这个作用域里面,不会污染到外部,也减少了阅读代码的成本.
同时这个lambda闭包中,也能访问到之前定义的一些变量
再看这个,如果不使用apply,我们的链式构造queryDSL就会被if else逻辑截断,但如果我们用了apply,就可以在合适的地方插入这段逻辑,而又保持condition的链式流转
其实加了inline后,这些代码最终生成出来的东西,和java其实是一样的,但是它能在语法级别,帮你组织你的代码和逻辑,这样你写出来的Kotlin代码就是立体的,而且给人一种有组织有纪律的感觉,不想java一条线撸下来.
如果对高阶函数有一点理解的,会知道这真的是非常简单的语法,在很多fp语言里面更是早就被用烂了的东西.
不过很多被oo洗脑多年的,无法意识到它带来的价值.
比如我们学的一些设计模式,什么策略模式,装饰模式等等,用起来别扭,写起来累人,但一旦我们打开了这扇思维的门,我们发现,原来这些是可以组装,传递的,根本就用不着oo那套奇技淫巧。
方法不再是我们逻辑的最小单元,函数可以作为参数,互相传递,组装,这可以让我们可以用另一种思维去组织我们的代码。
还有工厂模式,有些情况确实是需要一个集中管理比如DI,
但大多数时候,我们从duck type角度来看,根本就是花大力气用密码手段写了一份说明性文档而已。