函数声明

Kotlin使用fun来声明函数,其中返回值用:表示,参数和类型之间也用:表示。

下面来声明一个函数,接收一个Int参数,并返回一个Int结果:

//声明一个方法,接收一个Int类型的参数,返回一个Int类型的值
fun makeMoney(initial: Int) : Int{
    println("make money 996!")
    return initial * 10
}
var money = makeMoney(10)//调用函数

如果一个方法没有返回值,可以用Unit表示,等同于Java的void,比如这样写:

fun makeMoney(): Unit{
    println("work hard,make no money!")
}

在Kotlin中如果一个方法的返回值是Unit,则可以省略不写:

fun makeMoney2(){
    println("work hard,make no money!")
}

默认参数

默认参数是现代化编程语言必备的语法特色。你肯定和我一样,早就厌倦了Java的又臭又长的毫无意义的方法重载。假设我们要打印学生的信息,如果大部分学生的城市都是武汉,年龄都是18岁,那就可以用默认参数来定义:

fun printStudentInfo(name: String, age: Int = 18, city: String = "武汉"){
    println("姓名:$name 年龄:$age 城市:$city")
}
printStudentInfo(name = "李雷") //姓名:李雷 年龄:18 城市:武汉
printStudentInfo(name = "韩梅梅", age = 16) //姓名:韩梅梅 年龄:16 城市:武汉

在调用多个参数的函数时,强烈建议像上面那样使用命名参数传递,这样更具可读性,而且不需要关心参数传递的顺序。比如:

printStudentInfo(age = 16, name = "韩梅梅")

单表达式函数

如果函数有返回值并且只有单个表达式,可以省略大括号,加个=号,像这样简写:

fun square(p: Int) = p * p //无需写返回值类型,Kotlin会自动推断

可变参数

Kotlin当然支持可变参数,使用vararg来声明可变参数。

//可变参数ts,是作为数组类型传入函数
fun <T> asList(vararg ts: T): List<T> {
    val result = ArrayList<T>()
    for (t in ts) // ts is an Array
        result.add(t)
    return result
}
val list = asList(1, 2, 3)

函数式编程

Kotlin支持类似于JavaScript那样的函数式编程,函数可以赋值给一个变量,也可以作为参数传递。

//使用square变量记录匿名函数
var square = fun (p: Int): Int{
    return p * p
}
println(square(10))

//接收函数作为参数,onAnimationEnd是函数类型
fun doAnimation(duration: Long, onAnimationEnd: (time: Long)->Unit){
    //执行动画,动画执行结束后调用onAnimationEnd
    println("执行动画")
    onAnimationEnd(duration)
}
doAnimation(2000, { time ->
    println("animation end,time:$time")
})

如果最后一个参数是函数的话,Kotlin有一种更简洁的写法,可以将函数代码块直接拿到大括号外面写:

doAnimation(2000) { time ->
     println("animation end,time:$time")
}

像这种在大括号外面的函数,省略了参数和返回值类型,使用箭头链接方法体,写法极其简洁,又叫做Lambda表达式。

扩展函数

Kotlin的扩展函数是一个非常有特色并且实用的语法,可以让我们省去很多的工具类。它可以在不继承的情况下,增加一个类的功能,声明的语法是fun 类名.方法名()

比如:我们可以给String增加一个isPhone方法来判断自己是否是手机号:

fun String.isPhone(): Boolean{
    return length==11 //简单实现
}
"13899990000".isPhone() // true

再比如给一个Int增加一个方法isEven来判断自己是否是偶数:

fun Int.isEven(): Boolean {
    return this % 2 == 0
}
1.isEven() // false

有了扩展函数,我们几乎不用再写工具类,它比工具类调用起来会更简单,并且更自然,就好像是这个对象本身提供的方法一样。我们可以封装自己的扩展函数库,使用起来爽翻天。

中缀表达式

中缀表达式是一个特殊的函数,只不过调用的时候省略了.和小括号,多了个空格。它让Kotlin的函数更富有艺术感和魔力,使用infix来声明。

来看个例子,我们给String增加一个中缀方法爱(),这个方法接收一个String参数:

infix fun String.(p: String){
    println("这是一个中缀方法:${this}$p")
}
//调用中缀方法
"我""你" //这是一个中缀方法:我爱你

是一个String对象,调用方法,传入这个参数。

如果将上面的方法增加一个String返回值:

infix fun String.(p: String): String{
    println("这是一个中缀方法:${this}$p")
}
//我们可以一直爱到底...
"我""爸爸""妈妈""奶奶"

可见中缀表达式可以解放你的想象力,让方法调用看起来跟玩一样。你可以创造出很多有意思的东西。不过中缀表达式有一些限制:

  1. 它必须是一个类的成员函数或者扩展函数
  2. 只能接收一个参数
  3. 参数不能是可变参数,并且不能有默认值

Kotlin的中缀表达式可以让我们像说大白话一样进行编程(声明式编程),这种又叫做DSL。Kotlin标准库中的kotlinx.html大量使用了这种语法,我们可以这样写html:

html {
    body {
        div {

        }
    }
}

htmlbodydiv都是一个中缀方法,你可以试着实现一个简单的方法。

局部函数

Kotlin支持局部函数,即在一个函数内部再创建一个函数:

fun add(p: Int){
    fun abs(s: Int): Int{
        return if(s<0) -s else s
    }
    var absP = abs(p)
}

局部函数内声明的变量和数据都是局部作用域,出了局部函数就无法使用。

尾递归函数

有时候我们会写一些递归调用,在函数的最后一行进行递归的调用叫做尾递归。如果递归的次数过多,会造成栈溢出,因为每一次递归都会创建一个栈。Kotlin支持使用tailrec关键字来对尾递归进行自动优化,保证不会出现栈溢出。

我们只需要用tailrec修饰一个方法即可获得这种好处,无需额外写任何代码:

tailrec fun findGoodNumber(n: Int): Int{
    return if(n==100) n else findGoodNumber(n+1)
}

像上面的函数,使用了tailrec修饰,Kotlin会进行编译优化,生成一个基于循环的实现。大概类似下面这样:

fun findGoodNumber(n: Int): Int{
    var temp = n
    while (temp!=100){
        temp ++
    }
    return temp
}

Inline函数

我不打算直接解释什么叫内联函数,先看个例子。假设我们有一个User对象,需要对它进行一些列赋值之后,去调用它的say方法:

var user = User() // 创建User对象,不需要new关键字
user.age = 30
user.name = "李晓俊"
user.city = "武汉"
user.say()

上面的代码看起来稍显啰嗦,不够简洁。使用apply内联函数改写为:

//使用apply内联函数进行改写
User().apply {
    age = 30
    name = "李晓俊"
    city = "武汉"
    say()
}

是不是更加简洁明了了?

内联函数一般用来简化对某个对象进行一系列调用的场景。Kotlin提供了大量的内联函数:applyalsorunwith等,总结起来它们的作用大都是可以让我们对某个对象进行一顿操作然后返回这个对象或者Unit。并且内联函数不是真的函数调用,会被编译器编译为直接调用的代码,并不会有堆栈开销。

Kotlin的内联函数在项目中会被大量应用,用得最多的是withapply

更新时间: 6/1/2019, 11:25:02 AM