Kotlin从头开始(一):lambda表达式和高阶函数 一

2,246 阅读4分钟

lambda表达式

函数

先从熟悉的Java说起,在Java中,函数的形式一般是这样的:

public String getString(String input) {
    
}
  • 观察一下,有一个很明显的特点,就是先有输出,也就是返回值,类型为String,再有的输入,也就是参数。

而在Kotlin中,却是相反的,先有输入,再有输出,如下:

fun getString(input: String) : String {
    
}
  • 先写的参数,再有的返回值,相对符合写法习惯

Lambda函数

函数,一般包括两个部分,一部分是函数的声明,另一个部分是函数的实现。上述写的getString方法中,fun getString(input: String) : String是函数的声明,括号中的就是函数的实现。

那么Kotlin中, 怎么写一个函数的声明呢?

var getString : (String) -> String

是不是看的一头雾水,如果写出对应的Java函数的话,应该是如下的样子:

public String getString(String inputStr) 

对比不难发现,kotlin和java的输入,输出的类型,是可以对的上的,只不过是呈现的方式不同。

那如果返回的不是String,而是Any,对应Java中的返回值是什么呢?

答案就是Object。

tip:在Kotlin中,没有基本数据类型,只有引用数据类型(Int,Double...),但是在反编译成java字节码的时候,会变成int,double等。

到目前为止,只有函数的声明,还不能直接调用

如何写一个声明 + 实现的函数

var getString = {
    println("hello kotlin")
}
  • 如上,就是一个包括了声明和实现的函数 如果在kotlin中看不懂,我们写出同等的java代码看看,分析一下,功能就是简单的打印hello kotlin:
public void getString() {
    System.out.println("hello kotlin");
}

值得注意的是,Kotlin的类型推导帮助我们省略了函数的类型,其实写全了是如下的样子:

var getString : () -> Unit = {
    println("hello kotlin")
}

此处有没有一点疑惑,为什么类型推导得出的类型是 () -> Unit ?

此时只要回头看一下Kotlin中函数是如何声明的,就知道为什么了。

为什么这里的函数不是fun开头的

  • 其实这里的函数是匿名函数,上述的写法就是把这个匿名函数赋值给了变量。
  • 可以直接使用变量名进行调用或者使用invoke,方式如下:
fun main() {
    val getString : () -> Unit = {
        println("hello kotlin")
    }

    getString()
    getString.invoke()
}
  • 两种方式都可以成功调用,这里的()其实是invoke操作符的重载。

var getString = {"this is kotlin"} 是什么意思

跑一下上面的代码,看看运行结果是啥

fun main() {
    val getString = {
        "hello kotlin"
    }

    print(getString.invoke())
}
  • 结果打印了hello kotlin。
  • 在括号中,最后一行将会作为返回值。如果把代码修改为下面的样子
fun main() {
    val getString = {
        "hello kotlin"
        666
    }

    print(getString.invoke())
}

返回值就成了666。 如果最后一行是函数,则就成了函数中的函数,也就是高阶函数

稍微复杂一点

val testPlus = {number1: Int, number2: Int -> number1 + number2}
  • 这个函数中,number1和number2分别作为两个参数,函数的功能是number1+number2,则返回值的类型就是Int。相当于Java中的
public int testPlus(int number1, int number2) {
    return number1 + number2;
}

再复杂一点

刚刚提到了,函数分为声明和实现两个部分。那么自然就可以先声明再实现。

先声明:

val getString : (Int) -> String

再实现:

getString = fun(number) = number.toString()
  • 声明的时候,参数类型为Int,返回值为String。实现的时候,number会被自动推导为int,函数实现要返回String类型,如果toString换成toShort,编译器就会报错。

如果把成整体就是下面的样子:

fun main() {
    val getString : (Int) -> String = fun(number) = number.toString()
    getString
}

tip:上述代码中,把鼠标悬停在getString的调用上,按下Shift+Ctrl+P,就成看到getString的参数和返回值。

image.png

那么,这么写有什么好处呢?

除了装b,大概是没有其他用处的。

茴字的最后一种写法

声明和实现一起写,应该怎么写呢

val getString : (Int, String) -> String = {values, str -> "value is $values str is $str"}
  • values是Int类型,str是String类型,返回值是String类型

还能再简化一点吗

能,但是只能有一个参数

val getString : (String) -> Unit = {
    println("string  is $it")
}
  • 按照上面的说法, 大括号后面应该出现一个str->,但是因为只有一个参数,所以可以用it代替。

在kotlin中,大部分都是表达式,比如if,when,表达式可以返回,而在java中,if等都是语句,不能返回

那两个甚至多个参数的时候,可以简写吗

fun main() {

    val getString : (String, String) -> Unit = {_,str2 ->
        println("str is $str2")
    }
}
  • getString有两个形参,但是只要用第二个参数,则第一个参数就可以用_代替。
  • 这么写,在方法内部就不会生成第一个参数,减少空间的占用