首页 Kotlin学习笔记(16) - 函数的基础定义
文章
取消

Kotlin学习笔记(16) - 函数的基础定义

一、基本定义

每个参数必须有显式的类型,可使用=号提供默认值,减少重载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//无返回值 - java中会使用void表示无返回,kotlin中其实使用Unit表示,只不过可以省略掉,方法体内也不需要显式的返回。
fun read(
    b: ByteArray,
    off: Int = 0,
    len: Int = b.size,
) { /*……*/ }

//有返回值 - 有返回值时直接在方法后指定返回类型
fun read(
    b: ByteArray,
    off: Int = 0,
    len: Int = b.size,
) : String{ 
  return "xxxx"
}

//在方法覆盖时需要8⃣️默认值省略掉
open class A {
    open fun foo(i: Int = 10) { /*……*/ }
}

class B : A() {
    override fun foo(i: Int) { /*……*/ }  // 不能有默认值
}

//如果有默认值的参数位于没有默认值参数前面,那不可以直接省略,但可以通过指名参数的方式,也叫具名参数
fun foo(
    bar: Int = 0,
    baz: Int,
) { /*...*/ }

foo(baz = 1)

//如果最后一个参数是lambda表达式,那可以直接放到括号外使用大括号包裹
fun foo(
    bar: Int = 0,
    baz: Int = 1,
    qux: () -> Unit,
) { /*...*/ }

foo(1) { println("hello") }     // Uses the default value baz = 1
foo(qux = { println("hello") }) // Uses both default values bar = 0 and baz = 1
foo { println("hello") }        // Uses both default values bar = 0 and baz = 1

需要注意调用java函数时不可以使用具名参数,使用时注意参数顺序及是否包含默认值。

二、单表达式函数

当一个函数有单一表达式构成,可以将大括号直接省略掉,直接放在=号后面。如果类型是可推导的话,返回类型也可以省略掉,但这种推导只能用在单一表达式,不能用在代码块中,代码块中必须显式的指定类型。

1
2
3
fun double(x: Int): Int = x * 2
//可推导
fun double(x: Int) = x * 2

三、可变参数

可变参数的传递需要用*号进行修饰,通常放置在方法中的最后一个参数,且方法中只能有一个可变参数

1
2
3
fun foo(vararg strings: String) { /*……*/ }

foo(strings = *arrayOf("a", "b", "c"))

如果你想传入一个原生类型数组vararg 中,需要先通过 toTypedArray() 函数将其转换为常规的类型化数组

1
2
val a = intArrayOf(1, 2, 3) // IntArray is a primitive type array
val list = asList(-1, 0, *a.toTypedArray(), 4)

四、中缀表达式

使用 infix关键字修饰函数表示可以使用中缀形式调用,并需要满足以下条件

  • 必须是成员函数或扩展函数
  • 必须只有一个参数
  • 参数不可以是可变参数,并且不能有默认值
1
2
3
4
5
6
7
infix fun Int.shl(x: Int): Int { ... }

// calling the function using the infix notation
1 shl 2

// is the same as
1.shl(2)

需要注意下中缀运算的优先级

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#优先级是要低于 运算操作符、类型转换、rangeTo操作符

1 shl 2 + 3 is equivalent to 1 shl (2 + 3)

0 until n * 2 is equivalent to 0 until (n * 2)

xs union ys as Set<*> is equivalent to xs union (ys as Set<*>)

#优先级高于, &&、||、is、in 等一些操作服

a && b xor c is equivalent to a && (b xor c)

a xor b in c is equivalent to (a xor b) in c

还需要注意中缀表达式始终需要接收者和参数,即使在当前接收者上调用也需要显式的指定this

1
2
3
4
5
6
7
8
9
class MyStringCollection {
    infix fun add(s: String) { /*...*/ }

    fun build() {
        this add "abc"   // Correct
        add("abc")       // Correct
        //add "abc"        // Incorrect: the receiver must be specified
    }
}

五、函数作用域

1. 顶层函数

可以直接在文件顶层声明不需要专门为他创建一个类

2.局部函数

可以在函数内定义新的函数,内部函数可访问外部函数的变量

1
2
3
4
5
6
7
8
9
class MyStringCollection {
    infix fun add(s: String) { /*...*/ }

    fun build() {
        this add "abc"   // Correct
        add("abc")       // Correct
        //add "abc"        // Incorrect: the receiver must be specified
    }
}

3. 成员函数

直接定义在类或对象内部

1
2
3
class Sample {
    fun foo() { print("Foo") }
}

4. 泛型函数

1
fun <T> singletonList(item: T): List<T> { /*……*/ }

5. 尾调用(尾递归)函数

通常用来优化递归,在一些性能算法中常用到递归,可以使用尾调用函数,首先需要满足以下几个前提条件

  • 函数需要调用自身(递归)
  • 递归调用是函数的最后一个操作
  • 尾调用的结果直接返回,不能再进行其他操作
  • 不能用在 try,catch,finally块中
  • 不能用在open 函数上

当满足以上前提条件时,使用tailrec关键字修饰,就是一个尾调用函数,编译器会进行额外优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
val eps = 1E-10 // "good enough", could be 10^-15

tailrec fun findFixPoint(x: Double = 1.0): Double =
    if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))

//等同于以下代码
private fun findFixPoint(): Double {
    var x = 1.0
    while (true) {
        val y = Math.cos(x)
        if (Math.abs(x - y) < eps) return x
        x = Math.cos(x)
    }
}

本文由作者按照 CC BY 4.0 进行授权

Kotlin学习笔记(15) - 泛型

Jetpack 使用总结 - Room