Kotlin语法基础篇三:函数

848 阅读6分钟

 前言:

前两篇文章介绍了Kotlin中的基本数据类型属性和控制流。本篇文章我们将介绍Kotlin中比较重要的一个知识点:函数。对于函数,我想每个开发同学都不会陌生。善于将复杂的逻辑拆分成多个功能函数,将对我们代码的可读性和可维护性会有很大的帮助。而将多个复杂的逻辑放在同一个方法中,可能会让代码变得十分臃肿。下面让我们开始本篇文章的学习。

1.函数的声明

在Kotlin中我们用关键字fun来声明一个函数。一个简单的函数声明如下: 

fun sum(left: Int, right: Int): Int {
        return left + right
}

 关键字fun后面紧跟着函数名,通常函数名都是以这个函数实现的功能来命名的。函数名后紧跟着小括号(),定义该函数所需要的参数。即name:type。

多个参数用逗号隔开(name:type, name:type)。如果不需要参数,直接用()即可。如果该函数需要返回值则在()后面添加 :type。花括号{ }则代表这个函数的方法体或者说是这个函数的作用域。

2.函数参数的默认值

通常在Java中函数我们称之为方法,相比较Java语言,Kotlin中的函数允许有参数默认值。而允许方法的参数可以拥有默认值,可以减少方法的重载(方法名相同,方法的参数类型和个数不同,我们称之为方法的重载)。如下函数的定义:

fun sum(left: Int, right: Int): Int {
        return left + right
}

// right参数具有默认值0
fun sum(left: Int, center: Int, right: Int = 0): Int {
        return left +center + right
}

当我们给sum函数的参数right添加了默认值以后。在函数调用的地方,我们可以选择性的传或者不传这个参数。

fun main() {
    sum(0, 10)
    sum(0, 10, 15)
}

fun sum(left: Int, center: Int, right: Int = 0): Int {
    return left +center + right
}

善于使用函数参数的默认值,在实际开发过程中,可以减少像Java语言中的方法重载。

3.具名参数 

当我们调用一个拥有众多参数的函数时,参数类型和参数名匹配起来比较麻烦时。具名参数的使用就变得很有意义。使用具名参数可以不用考虑参数在函数中声明的顺序,使用propertyName = propertyValue的方式传入参数。假设我们有如下拥有多个参数的函数fold:

fun main() {
    fold(right = false, center = "center", left = 0, isEmpty = false)
}

fun fold(left: Int, center: String, right: Boolean, isEmpty: Boolean) {
    // ...省略逻辑
}

fold函数拥有4个参数,当我们在main函数中使用具名参数的方式调用fold函数,我们无需再考虑函数参数在声明时的位置,只要将所有的参数具名传入即可。当然在实际开发的过程中,一个复杂函数的参数远不与此。

4.函数的默认返回值Unit

当一个函数没有返回值时,它将拥有一个默认的返回值类型Unit。通常情况我们都会省略它。如下示例,我们将fold函数的返回值显示的声明成Unit

fun fold(initValue: Int):Unit {
    // ...省略逻辑
    return Unit
}

// 省略Unit
fun fold(initValue: Int) {
    // ...省略逻辑
    return 
}

 返回时,我们可以直接使用return,也可以显示的加上Unit返回值类型。

5.单函数表达式

在上一篇文章属性和控制流中我们介绍到,当if或者when表达式的分支块中仅有一行表达式时,我们可以省略分支块的花括号。而Kotlin中的函数亦是如此。当函数返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可:

fun sum(left: Int, right: Int): Int = left + right 

// Kotlin编译器可以推断出函数的返回值类型,可省略函数的返回值类型
fun sum(left: Int, right: Int) = left + right 

 既然if或者when也可以作为单个表达式,那么我们也可以将一个函数用=连接一个if或者when的表达式。

fun isEmpty(str:String) = if(str.length == 0) true else false

// 示例代码
fun findNum(num:Int) = when(num) {
    1001 -> 0
    1002 -> 1
    else -> -1
}

6.可变数量的参数

在Java中我们使用propertyType... propertyName的方式来声明一个可变数量的参数:

private void sum(Integer... value) {
        // ...
}

而在Kotlin中我们使用关键字vararg来声明可变数量的参数。一个方法只能有一个参数是用vararg修饰的。当我们调用 vararg-函数时,可以一个接一个地传参。如下示例代码:

fun main() {
    sum(1, 2, 3)
}

fun sum(vararg value:Int) {
    for(i in value) {
        println("i = $i")
    }
}

// 输出
i = 1
i = 2
i = 3

当一个vararg-函数有多个参数时,如果vararg参数不是最后一个参数,其后的参数可以使用具名参数的方式传递:

fun main() {
    sum(1, 2, 3, b = 10, c = 100)
}

private fun sum(vararg value: Int, b: Int, c: Int) { }

如果我们已经有一个数组并希望将其内容传给该函数,我们使用伸展(spread) 操作符(在数组前面加 *):

fun main() {
    val array = intArrayOf(1,2,3)
    sum(*array, b = 3, c = 4)
}

private fun sum(vararg value: Int, b: Int, c: Int) { }

7.中缀表示法 

在Kotlin中使用infix关键字修饰的函数可以使用中缀表示法,忽略该函数调用时的点和括号。该函数必须满足如下条件:

  • 用infix修饰的函数有且只能有一个参数
  • 必须是成员函数或者扩展函数(关于扩展函数后面会详细讲解)
  • 不能接受vararg声明的可变数量的参数

典型的应用,我们可以看Kotlin中给我们提供的Tuples.kt中的数据类Pair:

public data class Pair<out A, out B>(
    public val first: A,
    public val second: B
) : Serializable 
// ...

public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)

// ...

由上面的代码中我们可以看到数据类Pair拥有infix修饰的扩展函数to -> A.to(that: B)。下面我们来看一下在实际开发中的使用:

fun main() {
    val pair = 20 to "age"
    println("pair = $pair")
}

// 输出
pair = (20, age)

中缀表示法可以让我们的函数调用像阅读英文一样的优雅。使用A to B的方式创建了一个Pair对象,省略了调用函数时的点和括号。下面我们使用infix关键字来实现一个我们自己定义的函数:

infix fun <T> Collection<T>.has(item: T) = if (item in this) true else false

fun main() {
    val languageList = arrayListOf("kotlin", "ios", "android")
    if (languageList has "test") {
        println("There is this language")
    } else {
        println("There is no such language")
    }
}

// 输出
There is no such language

8.函数作用域

1.在Kotlin中函数可以在文件顶层声明。不需要像Java那样要创建一个类来保存一个函数。对与在文件顶层声明的函数默认情况下,我们可以在当前文件所在的项目中任意地方访问,除非你将函数显示的加上private访问修饰符。选中当前项目,右击鼠标或者触摸板。New -> Kotlin Class/File,在弹出的选择框中,我们选择File,声明一个Fun.kt的文件。

// 当前项目中可以调用
fun sum(left: Int, right: Int): Int {
    return left + right
}

// 当前文件中可以使用
private fun fold() {
    println("fold called")
}

2.成员函数。

在类内部或者对象内部创建的函数我们称之为成员函数,以点表示法调用。 

class Student {
    fun study() {
        println("I enjoy learning")
    }
}

fun main() {
    Student().study()
}

3.局部函数。(在Kotlin中支持一个函数嵌套另外一个函数)

在函数内部声明一个函数,我们就称这个函数为局部函数。如下示例,我们在main函数的内部声明了一个getMax函数。

fun main() {
    val a = 1001
    val b = 1002

    // 局部函数
    fun getMax() :Int {
        return if(a > b) {
            println("a = $a")
            a
        } else {
            println("b = $b")
            b
        }
    }

    println("max = ${getMax()}")
}

// 输出
b = 1002
max = 1002

局部函数可以访问外部函数声明的属性。 

9.泛型函数 

在Kotlin中我们通常在关键字fun之后紧跟<T>来声明一个泛型函数。(大写字母放在<>中,T是习惯性写法,也可以用别的大写字母表示),如下:

fun <T> getData(data:T) { 
    println("data = $data")
}

我们也可以将声明的泛型类型作为函数的返回值:

fun <T> getData(data:T) :T {
    println("data = $data")
    return data
}

 关于Kotlin中泛型的使用我们会在后续的文章中详细介绍。

 总结

到这里关于Kotlin中函数的介绍我们就写完了,下篇文章我们将详细介绍Kotlin中的高阶函数和Lambda表达式,我们下期再见。