快速入门Kotlin②控制流&函数

控制流

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

whiledo ..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。因为这样的函数在代码体中可能有复杂的控制流,并且返回类型对于读者(有时甚至对于编译器)是不明显的。

函数作用域&类型

  1. 顶层函数,无需创建类来保存函数。

  2. 局部作用域:嵌套函数。

  3. 成员函数:在类或对象内部定义的函数。

  4. 扩展函数:可以在不继承或修改类的情况下,为类添加额外的行为和功能。扩展函数是静态解析的,它并没有对类进行实际的修改或继承。扩展函数的作用域仅限于定义它的文件内。

  5. 泛型函数:可以在函数中使用类型参数的特殊函数,它增加了函数的灵活性和重用性。

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)
    }
}
相关推荐
xvch33 分钟前
Kotlin 2.1.0 入门教程(七)
android·kotlin
zhangphil3 小时前
Android BitmapShader简洁实现马赛克,Kotlin(一)
android·kotlin
五味香10 小时前
Java学习,查找List最大最小值
android·java·开发语言·python·学习·golang·kotlin
五味香1 天前
Java学习,List截取
android·java·开发语言·python·学习·golang·kotlin
xvch2 天前
Kotlin 2.1.0 入门教程(三)
android·kotlin
小李飞飞砖2 天前
kotlin的协程的基础概念
开发语言·前端·kotlin
深色風信子2 天前
Kotlin Bytedeco OpenCV 图像图像49 仿射变换 图像裁剪
opencv·kotlin·javacpp·bytedeco·仿射变换 图像裁剪
五味香3 天前
Java学习,List移动元素
android·java·开发语言·python·学习·kotlin·list
studyForMokey3 天前
【Android学习】Kotlin随笔
android·学习·kotlin
zhangphil4 天前
Android BitmapShader实现狙击瞄具十字交叉线准星,Kotlin
android·kotlin