写给 Java 开发者的 Kotlin 教程 (6) - 函数基础

函数是构成软件的基础块。我们今天就开始 Kotlin 旅程的第二站 - 函数
函数定义
我们都知道函数的本质也就是接受一些东西然后吐出一些东西,那最为重要的是 入参,出惨,然后又知道 Kotlin 是一门静态语言,那类型又很重要,那我们来看看 Kotlin 是如何申明函数的。
1 | fun avg(a: Double, b: Double): Double { |
1 | Double avg(Double a, Double b) { |
和 Java 略有不同
Kotlin申明函数使用关键字funKotlin的入参类型是后置的Kotlin的出参类型是后置的
所以我们通常定义一个函数都会使用以下的格式
1 | fun functionName(param1: Type1, param2: Type2,..., paramN: TypeN): Type { |
单行函数定义
和 java 略有不同,kotlin 有一些特殊的函数格式比如 单行函数
1 | fun avg(a: Double, b: Double) = (a + b)/2 |
Unit 类型
如果希望函数无返回值,可以采用 Unit 作为返回类型,这个和 java 的 void 一个含义
1 | fun printAverage(a: Double, b: Double): Unit { |
当然,默认如果这里不填写任何类型,默认即是 Unit,下面的代码和上面等价
1 | fun printAverage(a: Double, b: Double) { |
函数特性
函数默认参数
Kotlin 借鉴其他语言的实现,也支持函数的默认参数值。如下
1 | fun displayGreeting(message: String, name: String = "Guest") { |
这样我们就可以这样调用这个函数
1 | displayGreeting("Welcome to the Yann Blog", "John") |
考虑以下的定义
1 | fun arithmeticSeriesSum(a: Int = 1, n: Int, d: Int = 1): Int { |
那我们尝试用下面的方式调用,我们会发现失败
1 | arithmeticSeriesSum(10) // error: 缺少参数 |
因为我们的参数只能自左向右的传递入函数,所以我们可以这样调用
1 | arithmeticSeriesSum(1, 10) // 结果 = 55 |
所以我们在申明函数的时候,尽可能的将带默认值的参数放在函数的右侧。
命名参数
我们经常在 Python 的代码中看见,如下的定义
1 | def named_function(a, b=20, c=10): |
这种称之为 Named Arguments,Kotlin 也可以做到如何
1 | fun arithmeticSeriesSum(a: Int = 1, n: Int, d: Int = 1): Int { |
注意最后一种方式不被允许了,原因也很简单,因为一旦命名了,我们就无法得知后续的参数应该从哪里开始。
可变参数
和 Java 不同Kotlin采用一种特殊的关键字 vararg 来申明可变参数
1 | fun sumOfNumbers(vararg numbers: Double): Double { |
1 | Double sumOfNumbers(Double... numbers) { |
值得注意 在一个函数中只允许出现一个 vararg。
函数作用域
包级别作用域
比如下面这个函数的作用域就是这个包级别,也就是在同一个包里都可以访问这个函数。
1 | package maths |
比如我们可以这么调用
1 | package maths |
成员函数
和大部分的 Java 函数相同,我们可以在 类 声明函数
1 | class User(val firstName: String, val lastName: String) { |
嵌套函数
在 java 程序中,我们经常需要为了代码的可读性将一些列的行为抽象成一个独特的函数,这些函数往往会被定义为
private 但是往往被复用一次而已。比如下面这个 calculateBMI,对比之下,我个人更喜欢的是嵌套函数
1 | private Double calculateBMI(Double weightInKg, Double heightInCm) { |
1 | Double findBodyMassIndex(Double weightInKg, Double heightInCm) { |
1 | fun findBodyMassIndex(weightInKg: Double, heightInCm: Double): Double { |
内联函数
内联函数可以在编译器展开,减少 Function Stack 的使用,对于提高性能有 迷之作用
我们想要将一个函数设置为内联函数只需要如下声明即可:
1 | inline fun <T> lock(lock: Lock, body: () -> T): T { |
只需要在函数的前面增加一个 inline 关键字即可。
扩展函数
想想一个场景,我们需要给某个类型的对象增加一个函数,而这个类型的源码可能是在某个第三方的Jar内,我们仅仅能通过继承来实现我们的需求,而现在 kotlin 可以通过拓展函数来实现。
1 | fun MutableList<Int>.swap(index1: Int, index2: Int) { |
这样我们就为了 MutableList<Int> 增加一个新的函数 swap(index1: Int, index2: Int)
值得注意: 扩展是静态解析的,也就是说并不会因为运行时的状态而采用多态的函数进行调用。举个例子
1 | open class C |
函数泛型
既然有函数,怎么可能没有泛型呢?Kotlin 的泛型系统比 java 更加复杂,关于这块,我们将在一个独立的章节进行讲解。
1 | fun <T> singletonList(item: T): List<T> { |