函数是一个类型,就像编程语言中的基本类型一样,就像Java中的引用类型一样,函数是函数类型。基本类型、引用类型、函数类型,说白了物理空间就是内存的一段区域,逻辑空间叫什么基本类型、引用类型、函数类型。基本类型就是int, float这种存储数据的,引用类型就是类呗,一个类在内存中不也是一段区域嘛,函数类型就是一段操作流程呗,一段操作流程在内存中不也是一段区域嘛。总之一样啦。猫是动物,狗是动物,人家棕熊也是动物啦,都在地球上占据一定的空间,不能把人家当成空气。
既然他们都是一种类型,那就要同等对待,既可以作为变量,也可以作为常量;既可以作为函数形参,也可以作为函数返回值。猫和狗能结婚生子,人家棕熊也可以,不可以把人家阉割了。物物平等。
fun add(a: Int, b: Int) : Int {
return a + b
}
上面这个棕熊,哦,不对,函数,上面这个函数的类型是啥?
不知道吧,我告诉你,是这个:
(Int, Int) -> Int
惊不惊喜?咦,看不懂?回去补补lambda表达式去,老衲不负责给你补基础知识。哇咔咔!
好啦,既然函数有类型了,那我们用函数声明变量吧,它和用基本类型,用类声明变量是一样一样嘀。
var myfun : (Int, Int) -> Int //函数类型
var myBase : Float //基本类型
var myClass : String //引用类型
从此,江湖上出现了三剑客:基本类型、函数类型、引用类型。打遍天下无敌手。
定义完类型了,我得赋值啊,要不我定义类型干啥。
客官请看下面内容:
myfun = :: add //使用函数引用要用::,不能加(),因为加()就变成调用函数了,而不是使用函数引用
myBase = 1.5f
myClass = String("你瞅啥,我就喜欢用String的构造函数咋地") //实例化对象不需要用new了,直接用对象的构造函数
myClass = "当然,直接这样写也是可以的"
再来,函数类型作为形参:
fun sum(a: Int, b: Int, fn: (Int, Int) -> Int): Int {
return fn(a, b)
}
我就喜欢把函数类型传进去,咋嘀!
再来,函数类型作为返回值:
fun getMathFunc(type: String): (Int, Int)-> Int {
// 定义一个计算加法的局部函数
fun add(a: Int, b: Int) : Int {
return a + b;
}
// 定义一个计算减法的局部函数
fun sub(a: Int, b: Int) : Int {
return a - b;
}
// 定义一个计算乘法的局部函数
fun mul(a: Int, b: Int) : Int {
return a * b;
}
when(type) {
"add" -> return ::add
"sub" -> return ::sub
else -> return ::mul
}
}
fun main(args: Array) {
var mathFunc = getMathFunc("add") //得到add函数
println(mathFunc(5, 3)) //输出8
mathFunc = getMathFunc("sub") //得到sub函数
println(mathFunc(5, 3)) //输出2
mathFunc = getMathFunc("mul") //得到mul函数
println(mathFunc(5, 3)) //输出15
}
好啦,就说到这里吧,既然函数又能作为变量,又能作为形参,又能作为返回值,说不定还能作为常量(我猜测地,不过函数作为常量干啥,函数本身就是变化地,作为常量干啥,哈哈哈,完全没用,就像你把类实例化的对象作为常量干啥,也没用哇),那么函数这么厉害,和其他类型一样,就要给它和之前我们简单使用的函数做出区分,至少在名词上做出区分,这样才让人感觉高大上。记住,叫“高阶函数”!这个不重要,忘了也没事,别在意,就是个多余的名字嘛,之前的函数不被阉割掉,也不会出现这么个多余的名字。好吧,你可以这么理解,之前世界上的棕熊都是被阉割的,现在的棕熊才是正常的,和猫啊,狗啊一样,都能结婚生子。阿弥陀佛~~
我对自己是有要求嘀,是要求自己尽量写出更优雅的代码嘀,再来:
上面的局部函数太繁琐,替换成lambda表达式:
fun getMathFunc(type: String): (Int, Int)-> Int {
when(type) {
"add" -> return {a: Int, b: Int -> a + b}
"sub" -> return {a: Int, b: Int -> a - b}
else -> return {a: Int, b: Int -> a * b}
}
}
fun main(args: Array) {
var mathFunc = getMathFunc("add") //得到add函数
println(mathFunc(5, 3)) //输出8
mathFunc = getMathFunc("sub") //得到sub函数
println(mathFunc(5, 3)) //输出2
mathFunc = getMathFunc("mul") //得到mul函数
println(mathFunc(5, 3)) //输出15
}
另外,lambda表达式是可以根据上下文推断类型的,所以,我们还可以更简化,再来:
fun getMathFunc(type: String): (Int, Int)-> Int {
when(type) {
"add" -> return {a, b -> a + b}
"sub" -> return {a, b -> a - b}
else -> return {a, b -> a * b}
}
}
fun main(args: Array) {
var mathFunc = getMathFunc("add") //得到add函数
println(mathFunc(5, 3)) //输出8
mathFunc = getMathFunc("sub") //得到sub函数
println(mathFunc(5, 3)) //输出2
mathFunc = getMathFunc("mul") //得到mul函数
println(mathFunc(5, 3)) //输出15
}
终于可以OVER了~~~
且慢,谢谢颜文大神的指点,getMathFunc函数还可以使用“=”来赋值。
fun getMathFunc(type: String):(Int, Int) -> Int = when(type) {
"add" -> {a, b -> a + b}
"sub" -> {a, b -> a - b}
else -> {a, b -> a * b}
}
可以使用“=”赋值的有表达式和匿名函数,when语句可以这样写的话,我理解when语句应该是表达式吧?是吧?是吧?
查了下资料,果然如此。Kotlin中的if语句和when语句都可以作为表达式。
if语句作为if表达式时,这样写:
var str = if (age > 20) "age 大于20" else if(age < 20) "age小于20" else "age等于20"
when语句作为when表达式时,这样写:
var str = when (score) {
'A' -> {
println("望百尺竿头更进一步")
"优秀"
}
'B' -> "良好"
'C' -> "中"
'D' -> "及格"
else -> "不及格"
}
让我们再总结一下吧,表达式也可以给变量、常量赋值。
再让我们看下前面的例子吧,都哪些内容可以给变量赋值呢?
var myfun = :: add //使用函数引用要用::,不能加(),因为加()就变成调用函数了,而不是使用函数引用
var myBase = 1.5f
var myClass = String("你瞅啥,我就喜欢用String的构造函数咋地") //实例化对象不需要用new了,直接用对象的构造函数
// var myClass = "当然,直接这样写也是可以的"
var myExpression = if (age > 20) "age 大于20" else if(age < 20) "age小于20" else "age等于20" //表达式也可以直接赋值哦
相比于前面,又多个一种可以给变量赋值的,就是表达式。
再让我们从另一个角度考虑,既然变量声明可以用var表示,函数声明用fun表示,而所谓的声明,就是给起个名字而已,从栈中发出一个指针。变量var声明的指针可以指向常量、函数、对象、表达式,而函数fun声明的指针指向的只有函数么?不是的,函数声明的指针还可以指向表达式。细心的读者可以看到我前面的例子中,表达式也是可以给函数类型赋值的。
将if表达式赋给函数类型:
fun getMathFunc(age: Int):String = if (age > 20) "age大于20" else if(age < 20) "age小于20" else "age等于20"
fun main(args: Array) {
println(getMathFunc(25)) //输出“age大于20”
}
将when表达式赋给函数类型:
fun getMathFunc(type: String):(Int, Int) -> Int = when(type) {
"add" -> {a, b -> a + b}
"sub" -> {a, b -> a - b}
else -> {a, b -> a * b}
}
用表达式来给函数赋值也有一个名字,叫单表达式函数。
接着让我们再开一下脑洞,既然我前面说了基本类型、函数类型、类的引用类型都是一样一样的东东,那么表达式是否可以给类的引用类型赋值呢?
反正Java8中的lambda表达式是给函数式接口赋值的,不过那是因为Java8的lambda表达式不是纯函数,而是通过函数式接口实现的,所以对于Kotlin的表达式,在这方面也没有什么类比价值。
留给你去思考吧。欢迎留言讨论。
最后,让我们再想想,既然表达式能够赋值给变量,我们还赋值给函数干嘛?这不是多此一举嘛。
这个要从变量和函数的作用说起,函数是一段代码的封装,有传参用的形参和返回值,比如when表达式括号中的参数,可以是函数的形参传过来的。而如果赋值给变量的话,when表达式括号中的参数就必须在给该变量赋值之前指定。这完全是一种顺序化的代码执行逻辑,而不是函数调用逻辑。所以说,两者在功能上是不一样的。
请看下面代码:
//赋值给变量
var type = "add"
var mathFunc2:(Int, Int) -> Int = when(type) {
"add" -> {a, b -> a + b}
"sub" -> {a, b -> a - b}
else -> {a, b -> a * b}
}
//赋值给函数
fun getMathFunc(type: String):(Int, Int) -> Int = when(type) {
"add" -> {a, b -> a + b}
"sub" -> {a, b -> a - b}
else -> {a, b -> a * b}
}
fun main(args: Array) {
var mathFunc = getMathFunc("add") //得到add函数
println(mathFunc(5, 3)) //输出8
println(mathFunc2(5, 3)); // 输出8
}
就到这里吧。