控制流
if
if
是一个表达式,可以用于条件判断和控制流。
Kotlin
fun main() {
// 传统用法
val a = 1
val b = 2
var max = a
if (a < b) max = b
// With else
var max2: Int
if (a > b) {
max2 = a
} else {
max2 = b
}
// 作为表达式
val max3 = if (a > b) a else b
}
when
when 表达式取代了Java 的 switch 语句,可以用任意表达式(而不只是常量,比如in/is/函数)作为分支条件。
Kotlin
fun main() {
val obj: Any = ""
val result = when (obj) {
1 -> "One"
//假如很多分支需要用相同的方式处理,则可以把多个分支条件放在一起
2, 3 -> "Two or three"
"Hello" -> "Greeting"
//任意表达式(而不只是常量)作为分支条件(in/is/函数)
is Long -> "Long"
!is String -> "Not a string"
else -> "Unknown"
}
}
for
for 循环可以对任何提供迭代器(iterator)的对象进行遍历。
Kotlin
fun main() {
val list = listOf("apple", "banana", "otherfruit")
for (item in list) {
println(item)
}
//通过索引遍历一个数组或者一个 list
for (index in list.indices) {
println("item at $index is ${list[index]}")
}
//区间表达式
for (i in 1..3) {
println(i)
}
for (i in 6 downTo 0 step 2) {
println(i)
}
}
while
while 与 do ..while 照常使用。
Kotlin
fun main() {
//while 与 do..while 照常使用
val list = listOf("apple", "banana", "otherfruit")
var index = 0
while (index < list.size) {
println("item at $index is ${list[index]}")
index++
}
index = 0
do {
println("item at $index is ${list[index]}")
index++
}while (index < list.size)
}
返回和跳转
Kotlin 有三种结构化跳转表达式:return、break和continue。
-
return 。默认从最直接包围它的函数或者++匿名函数++返回。
-
break。终止最直接包围它的循环。
-
continue。继续下一次最直接包围它的循环。
Kotlin
fun main() {
findNumberByReturn(3)
forByBreak();
forByContinue()
}
fun findNumberByReturn(target: Int): Boolean {
val numbers = listOf(1, 2, 3, 4, 5)
for (number in numbers) {
if (target == number) {
return true // 结束函数并返回找到的值
}
println(number)
}
return false // 循环结束后未找到目标值,返回 -1
}
fun forByBreak() {
val numbers = listOf(1, 2, 3, 4, 5)
for (number in numbers) {
if (number == 3) {
break // 循环终止
}
println(number)
}
//输出:1 2
}
fun forByContinue() {
val numbers = listOf(1, 2, 3, 4, 5)
for (number in numbers) {
if (number == 3) {
continue // 跳过当前迭代,继续下一次迭代
}
println(number)
}
//输出:1 2 4 5
}
函数
参数
参数:函数参数定义 name: type。参数用逗号隔开,每个参数必须有显式类型。 默认参数 :函数参数可以有默认值,当省略相应的参数时使用默认值。 具名参数:具名参数是一种在函数调用时指定参数名称的方式。在函数调用时使用 参数名 = 值 的形式来传递参数。
可变参数:可以用 vararg
修饰符标记,通常是最后一个参数。允许将可变数量的参数传递给函数。 默认参数特殊Case:如果在默认参数之后的最后一个参数是 lambda 表达式,那么它既可以作为具名参数在括号内传入,也可以在括号外传入。
Kotlin
fun main() {
println(getResult(2, 3)) //输出:6
//默认参数
println(getResult(2)) //输出:2
//具名参数
foo(baz = 1) // 输出:bar is 0, baz is 1
//可变参数
println(asList(1, 2, 3)) //输出:[1, 2, 3]
}
// Kotlin 中的函数使用 fun 关键字声明。
fun getResult(x: Int, y: Int = 1) : Int {
return x * y
}
//具名参数举例
fun foo(
bar: Int = 0,
baz: Int,
) {
println("bar is $bar, baz is $baz")
}
fun<T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) {
result.add(t)
}
return result
}
返回类型
具有块代码体的函数必须始终显式指定返回类型,除非他们旨在返回 Unit
。因为这样的函数在代码体中可能有复杂的控制流,并且返回类型对于读者(有时甚至对于编译器)是不明显的。
函数作用域&类型
-
顶层函数,无需创建类来保存函数。
-
局部作用域:嵌套函数。
-
成员函数:在类或对象内部定义的函数。
-
扩展函数:可以在不继承或修改类的情况下,为类添加额外的行为和功能。扩展函数是静态解析的,它并没有对类进行实际的修改或继承。扩展函数的作用域仅限于定义它的文件内。
-
泛型函数:可以在函数中使用类型参数的特殊函数,它增加了函数的灵活性和重用性。
Kotlin
//顶层函数,无需创建类来保存函数。
fun main() {
Sample().foo() // 创建类 Sample 实例并调用 foo,输出:Foo
outerFunction() // 嵌套函数。输出:Outer function Nested function
val str = "hello"
println(str.reverseStr()) //输出:olleh
println(createList("apple", "banana", "orange")) //输出:[apple, banana, orange]
println(createList(1, 2, 3, 4, 5)) //输出:[1, 2, 3, 4, 5]
}
class Sample {
//成员函数:类或对象内部定义的函数。
fun foo() { println("Foo") }
}
fun outerFunction() {
println("Outer function")
//嵌套函数:可以在外部函数内部进行封装,并且只在外部函数内部可见和使用。
fun nestedFunction() {
println("Nested function")
}
nestedFunction()
}
// 扩展函数:将字符串反转
fun String.reverseStr(): String {
return this.reversed()
}
//泛型函数: `createList` 是一个泛型函数,它接受可变数量的参数 `items`,类型为 `T`。函数的返回类型为 `List<T>`,即根据传递的参数类型返回相应类型的列表。
fun <T> createList(vararg items: T) : List<T> {
return items.toList()
}
高阶函数和lambda 表达式
高阶函数
是将函数用作参数或返回值的函数,更加灵活。
-
map: 函数用于对集合中的每个元素应用给定的转换函数,并返回新集合。
-
filter:函数用于根据给定的条件函数过滤集合中的元素,返回新集合。
-
forEach: 函数用于对集合中的每个元素应用给定的操作函数,没有返回值。
Kotlin
fun main() {
val numbers = listOf(1, 2, 3, 4, 5)
val convertNumbers = numbers.map { it * it }
val evenNumbers = numbers.filter { it % 2 == 0 }
println(convertNumbers) //输出:[1, 4, 9, 16, 25]
println(evenNumbers) //输出:[2, 4]
numbers.forEach { print(it) } //输出:12345
}
函数类型
函数类型声明指定了函数参数和返回值的类型,以及函数的参数个数和参数类型。语法如下:
(parameters) -> returnType
-
parameters
是函数的参数列表,可以包含零个或多个参数,在声明中使用参数名和类型来指定。 -
returnType
是函数的返回值类型,用于指定函数的返回结果类型。
常见的函数类型声明如下:
Kotlin
//不带参数的函数类型
() -> Unit
//带有一个整数参数的函数类型
(Int) -> Unit
//带有两个字符串参数和一个整数返回值的函数类型
(String, String) -> Int
//带有一个整数参数和一个字符串返回值的函数类型
(Int) -> String
代码示例:
Kotlin
fun main() {
applyFunction(::square) //输出结果 result: 1764
}
/**
* applyFunction 是一个高阶函数,它接受一个函数类型参数 function。
* applyFunction 函数内部调用传递的函数类型参数,并将结果打印出来。
*/
fun applyFunction(function: (Int) -> Int) {
val result = function(42)
println("result: $result")
}
/**
* 我们将 square 函数作为参数传递给 applyFunction 函数。
*/
fun square(x :Int) : Int {
return x * x
}
Lambda 表达式
Lambda 表达式可以理解为轻量级的函数字面量,提供更简洁的语法。
基本语法如下:
Kotlin
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
-
lambda 表达式总是括在花括号中。
-
完整语法形式的参数声明放在花括号内,并有可选的类型标注。
-
函数体跟在一个
->
之后。 -
如果推断出的该 lambda 的返回类型不是
Unit
,那么该 lambda 主体中的最后一个(或可能是单个)表达式会视为返回值。
可以优化为:
Kotlin
val sum = { x: Int, y: Int -> x + y }
代码示例:
Kotlin
fun main() {
//Lambda 表达式没有参数
//定义了一个没有参数的 Lambda 表达式,并将其赋值给类型为 () -> Unit 的变量 greet。
val great = { println("hello, kotlin") }
great() //输出:hello, kotlin
//Lambda 表达式带有一个整数参数
//定义了一个带有一个整数参数的 Lambda 表达式,并将其赋值给类型为 (Int) -> Int 的变量 square
val square: (Int) -> Int = { y -> y * y }
println(square(5)) //输出:25
//Lambda 表达式带有多个参数
//定义了一个带有两个整数参数的 Lambda 表达式,并将其赋值给类型为 (Int, Int) -> Int 的变量 add。
val add = { x: Int, y: Int -> x + y }
println(add(3, 7)) //输出:10
//亦或者写为:
val add1: (Int, Int) -> Int = { x, y -> x + y }
println(add1(3, 7)) //输出:10
}
最后一个参数是 Lambda 表达式:可以使用末尾 Lambda 表达式的语法糖。这种语法糖允许我们在调用函数时将 Lambda 表达式放在圆括号外部,增强了代码的可读性。
Kotlin
fun performOperation(value: Int, operation: (Int) -> Int): Int {
return operation(value)
}
fun main() {
val result = performOperation(5) { number ->
number * 2
}
println("Result: $result") //输出:Result: 10
}
匿名函数
Lambda表达式通常比匿名函数更简洁。但匿名函数在某些情况下可能更适合,比如需要显式指定返回类型或需要包含多个语句的情况。
基本语法:
Kotlin
fun(x: Int, y: Int): Int {
return x + y
}
//可以优化为:
//显式指定返回值为Int
fun(x: Int, y: Int): Int = x + y
代码示例:
Kotlin
fun main() {
val numbers = listOf(1, 2, 3, 4, 5)
// 匿名函数接受一个整数参数 num,并在函数体中定义了两个语句。
// 首先,我们计算出 isEven 变量,表示数字是否为偶数。
// 然后,我们返回一个布尔值,判断数字既是偶数又大于 2。
// 通过使用匿名函数,我们可以在函数体中包含多个语句,更好地处理复杂的过滤逻辑。
// fun(num) : Boolean显式指定了返回类型Boolean;多条语句情况下匿名函数语义更清晰
val filteredNumbersAnonymous = numbers.filter (fun(num) : Boolean {
val isEven = num % 2 == 0
return isEven && num > 2
})
// Lambda 表达式的函数体与匿名函数相同,包含了多个语句。
// Lambda 表达式的语法更为简洁,但在这种情况下,匿名函数的定义也相对清晰。
val filteredNumbersLambda = numbers.filter { num ->
val isEven = num % 2 == 0
isEven && num >2
}
println("Filtered numbers: $filteredNumbersAnonymous") //输出:Filtered numbers: [4]
println("Filtered numbers: $filteredNumbersLambda") //输出:Filtered numbers: [4]
}
内联函数
当一个函数被声明为 inline
时,编译器会在调用该函数的地方将函数的实际代码复制到调用处,而不是通过函数调用的方式进行执行。
优点:
- 减少函数调用的开销:函数调用会涉及到栈帧的创建、参数传递等操作,而通过函数内联可以避免这些开销,提高代码的执行效率。
缺点:
-
不宜过度使用,可能会导致代码膨胀:内联函数的复制会增加代码的大小,因此在频繁调用的小函数上使用
inline
可能会导致代码膨胀,影响包的大小和性能。 -
不可内联的情况:某些情况下,编译器无法进行函数内联,例如递归函数、函数体包含函数表达式、函数参数被函数引用等。
代码举例:
Kotlin
//我们定义了一个名为 measureTime 的 inline 函数。该函数接受一个函数类型的参数 execute
inline fun measureTime(execute: () -> Unit) {
//函数体内部,我们记录了开始时间,执行了传递的代码块 block,然后记录了结束时间,并打印出代码块的执行时间。
val startTime = System.currentTimeMillis()
execute()
val endTime = System.currentTimeMillis()
println("Execution time: ${endTime - startTime} milliseconds")
//输出:Execution time: 1005 milliseconds
}
fun main() {
//编译时会将函数调用处的代码块直接替换到函数体内部,而不会创建额外的函数调用。这样可以减少函数调用的开销,并且在一些情况下可以提高性能。
measureTime {
// 执行一些耗时的操作
Thread.sleep(1000)
}
}