
2.1.2 函数
1.返回Unit的函数
跟Java不同,Kotlin没有void,但是函数总会返回一个值。如果一个函数不返回任何类型的对象,那么该函数返回的是Unit类型。
例如:
fun printHello(): Unit { println("Hello World") }
Unit返回值可以被省略:
fun printHello() { println("Hello World") }
2.返回Nothing的函数
跟Unit相比,容易混淆的是Nothing。Unit会返回Unit的单例,而通过阅读Nothing的源码,发现它永远都不会返回任何东西。

在Nothing的表达式之后,所有代码都是无法执行的。throw表达式的类型是一个特殊的类型Nothing。
下面定义的doForever函数返回Nothing类型,因此它后面打印的"done"语句永远不会被执行。

3.单表达式函数
当函数返回单个表达式时,可以省略函数体的花括号,例如:
fun sum(x: Int=0, y: Int): Int { return x + y }
等价于:
fun sum(x: Int=0, y: Int): Int = x + y
它还等价于:
fun sum(x: Int=0, y: Int) = x + y
因为Kotlin可以通过编译器来推断该函数的返回类型。
4.成员函数
成员函数是指在类或对象内部定义的函数,这一点跟Java中的概念是一致的。
5.局部函数(Local Function)
所谓局部函数,是指在一个函数中定义另一个函数。有点类似于内部类,局部函数可以访问外部函数的局部变量,甚至是闭包。
例如下面的代码,在validate函数中定义了validateInput函数,validateInput函数用来进行字符串的校验,帮助我们消除重复的代码。另外,还定义了一个printPerson函数,它也包含一个局部函数print()用于打印Person信息。print()函数直接访问了它的外部函数printPerson()的局部变量。

6.尾递归函数
来自维基百科的定义:在计算机科学中,尾调用是指一个函数的最后一个动作是一个函数调用的情形,即这个调用的返回值直接被当前函数返回的情形。这种情形下称该调用位置为尾位置。若这个函数在尾位置调用本身(或者一个尾调用本身的其他函数等),则称这种情况为尾递归,是递归的一种特殊情形。尾调用不一定是递归调用,但是尾递归特别有用,也比较容易实现。
使用递归对自然数求和:

执行上述代码,会出现“Exception in thread "main" java.lang.StackOverflowError”。
这是因为在Kotlin中使用尾递归函数需要满足两个条件:
· 使用tailrec关键词修饰函数。
· 在函数最后进行递归调用。
使用tailrec关键词之后,编译器会优化该递归,从而避免堆栈溢出的风险。
对上述代码稍作修改:

此时,执行结果如下:
705082704
而我们目前使用比较多的Java 8并不能在编译器级别直接支持尾部调用优化,只能通过Lambda方式实现。
7.Top level函数
相对于在Java中所有的内容都必须在类中定义,Kotlin允许直接在.kt文件中定义任何类定义之外的top-level函数。
top-level函数的默认修饰符是public,因此这些函数可以在任何位置被访问。例如定义如下的函数:
@Throws(IOException::class) fun readFileToString(file: File): String = file.readText()
Java程序想要调用readFileToString()方法,必须通过文件名Kt.readFileToString(f)才可以调用。或者在文件头添加:
@file:JvmName("FileUtils")
这样一来,Java程序可以通过FileUtils.readFileToString(f)进行调用。
让我们来小结一下Kotlin相关的top-level,如表2-1所示。
表2-1 Kotlin相关的top-level

8.无参的main函数
Kotlin程序的入口一般会采用main函数,例如:
fun main(args: Array<String>) { println("Hello Kotlin") }
Kotlin 1.3之后引入了一种更简单的无参main函数,简化了main函数的写法:
fun main() { println("Hello Kotlin") }