控制流
if
if
是一个表达式,可以用于条件判断和控制流。
fun main() {// 传统用法val a = 1val b = 2var max = aif (a < b) max = b// With elsevar max2: Intif (a > b) {max2 = a} else {max2 = b}// 作为表达式val max3 = if (a > b) a else b
}
when
when 表达式取代了Java 的 switch 语句,可以用任意表达式(而不只是常量,比如in/is/函数)作为分支条件。
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)的对象进行遍历。
fun main() {val list = listOf("apple", "banana", "otherfruit")for (item in list) {println(item)}//通过索引遍历一个数组或者一个 listfor (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 照常使用。
fun main() {//while 与 do..while 照常使用val list = listOf("apple", "banana", "otherfruit")var index = 0while (index < list.size) {println("item at $index is ${list[index]}")index++}index = 0do {println("item at $index is ${list[index]}")index++}while (index < list.size)
}
返回和跳转
Kotlin 有三种结构化跳转表达式:return、break和continue。
-
return。默认从最直接包围它的函数或者匿名函数返回。
-
break。终止最直接包围它的循环。
-
continue。继续下一次最直接包围它的循环。
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 表达式,那么它既可以作为具名参数在括号内传入,也可以在括号外传入。
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
。因为这样的函数在代码体中可能有复杂的控制流,并且返回类型对于读者(有时甚至对于编译器)是不明显的。
函数作用域&类型
1. 顶层函数,无需创建类来保存函数。
2. 局部作用域:嵌套函数。
3. 成员函数:在类或对象内部定义的函数。
4. 扩展函数:可以在不继承或修改类的情况下,为类添加额外的行为和功能。扩展函数是静态解析的,它并没有对类进行实际的修改或继承。扩展函数的作用域仅限于定义它的文件内。
5. 泛型函数:可以在函数中使用类型参数的特殊函数,它增加了函数的灵活性和重用性。
//顶层函数,无需创建类来保存函数。
fun main() {Sample().foo() // 创建类 Sample 实例并调用 foo,输出:FooouterFunction() // 嵌套函数。输出:Outer function Nested functionval str = "hello"println(str.reverseStr()) //输出:ollehprintln(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: 函数用于对集合中的每个元素应用给定的操作函数,没有返回值。
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
是函数的返回值类型,用于指定函数的返回结果类型。
常见的函数类型声明如下:
//不带参数的函数类型
() -> Unit
//带有一个整数参数的函数类型
(Int) -> Unit
//带有两个字符串参数和一个整数返回值的函数类型
(String, String) -> Int
//带有一个整数参数和一个字符串返回值的函数类型
(Int) -> String
代码示例:
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 表达式可以理解为轻量级的函数字面量,提供更简洁的语法。
基本语法如下:
val sum: (Int, Int) -> Int = { x: Int, y: Int -> x + y }
-
lambda 表达式总是括在花括号中。
-
完整语法形式的参数声明放在花括号内,并有可选的类型标注。
-
函数体跟在一个
->
之后。 -
如果推断出的该 lambda 的返回类型不是
Unit
,那么该 lambda 主体中的最后一个(或可能是单个)表达式会视为返回值。
可以优化为:
val sum = { x: Int, y: Int -> x + y }
代码示例:
fun main() {//Lambda 表达式没有参数//定义了一个没有参数的 Lambda 表达式,并将其赋值给类型为 () -> Unit 的变量 greet。val great = { println("hello, kotlin") }great() //输出:hello, kotlin//Lambda 表达式带有一个整数参数//定义了一个带有一个整数参数的 Lambda 表达式,并将其赋值给类型为 (Int) -> Int 的变量 squareval 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 表达式放在圆括号外部,增强了代码的可读性。
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表达式通常比匿名函数更简洁。但匿名函数在某些情况下可能更适合,比如需要显式指定返回类型或需要包含多个语句的情况。
基本语法:
fun(x: Int, y: Int): Int {return x + y
}//可以优化为:
//显式指定返回值为Int
fun(x: Int, y: Int): Int = x + y
代码示例:
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 == 0return isEven && num > 2})// Lambda 表达式的函数体与匿名函数相同,包含了多个语句。// Lambda 表达式的语法更为简洁,但在这种情况下,匿名函数的定义也相对清晰。val filteredNumbersLambda = numbers.filter { num ->val isEven = num % 2 == 0isEven && num >2}println("Filtered numbers: $filteredNumbersAnonymous") //输出:Filtered numbers: [4]println("Filtered numbers: $filteredNumbersLambda") //输出:Filtered numbers: [4]
}
内联函数
当一个函数被声明为 inline
时,编译器会在调用该函数的地方将函数的实际代码复制到调用处,而不是通过函数调用的方式进行执行。
优点:
-
减少函数调用的开销:函数调用会涉及到栈帧的创建、参数传递等操作,而通过函数内联可以避免这些开销,提高代码的执行效率。
缺点:
-
不宜过度使用,可能会导致代码膨胀:内联函数的复制会增加代码的大小,因此在频繁调用的小函数上使用
inline
可能会导致代码膨胀,影响包的大小和性能。 -
不可内联的情况:某些情况下,编译器无法进行函数内联,例如递归函数、函数体包含函数表达式、函数参数被函数引用等。
代码举例:
//我们定义了一个名为 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)}
}