Kotlin培训

939 阅读10分钟

培训的目的

怎么在已有项目中快速上手kotlin

一、怎么在已有项目中配置kotlin

image.png

image.png

二、基本语法

2.1 包定义

Kotlin 中的代码可以组织成多个包。一个文件可以包含多个类或函数,每个文件也可以有自己的包定义。包定义的语法如下:
// 文件 MyFile.kt 中的包定义
package com.benben.demo.bean
 // 动态列表
class Dynamic
 // 动态详情
class DynamicDetails

2.2 类

2.2.1 定义:

2.2.1.1 方式一

class Person {
    var name: String = ""
    var age: Int = 0
    
    fun sayHello() {
        println("Hello, my name is $name and I am $age years old.")
    }
}

在上面的示例中,我们定义了一个名为 Person 的类,它有两个属性 nameage,以及一个方法 sayHello()

在 Kotlin 中,类和属性默认都是 public 可见性,方法默认是 public 可见性且 final 类型(即不能被子类重写),可以使用 open 关键字来允许子类重写。

2.2.1.2 方法二

class Person {
    var name: String = ""
    var age: Int = 0
    
    constructor(name: String, age: Int) {
        this.name = name
        this.age = age
    }
    
    // ...
}

class Person(var name: String = "",
    var age: Int = 0 )
    

类的构造函数可以使用 constructor 关键字来定义,也可以直接在类名后面加上参数列表来定义。例如:

2.2.2 分类

2.2.2.1 普通类(normal class):定义类时不使用 open 关键字,不能被继承,也没有子类。是最基本的类定义形式。

class Person {
    var name: String = ""
    var age: Int = 0
    
    fun sayHello() {
        println("Hello, my name is $name and I am $age years old.")
    }
}

2.2.2.2. 抽象类(abstract class):定义类时使用 abstract 关键字,不能被实例化,只能被子类继承并实现抽象方法或属性。

abstract class Person {
    var name: String = ""
    var age: Int = 0
    
    fun sayHello() {
        println("Hello, my name is $name and I am $age years old.")
    }
}

2.2.2.3. 接口(interface):定义类时使用 interface 关键字,不能被实例化,只能被类实现。接口可以包含抽象方法、常量和默认实现的方法。

interface Person {
    fun sayHello()
}

2.2.2.4. 内部类(inner class):在类中定义的类,可以访问外部类的成员变量和方法。

class Person {
    var name: String = ""
    var age: Int = 0
    
    fun sayHello() {
        println("Hello, my name is $name and I am $age years old.")
    }
}

2.2.2.5. 嵌套类(nested class):在类中定义的类,不能访问外部类的成员变量和方法。

nested class Person {
    var name: String = ""
    var age: Int = 0
    
    fun sayHello() {
        println("Hello, my name is $name and I am $age years old.")
    }
}

2.2.2.6 数据类(data class):用于表示仅包含数据的类,Kotlin 自动生成 equals()、hashCode()、toString() 等方法,也可以使用 copy() 方法复制对象。

data Person {
    var name: String = ""
    var age: Int = 0
    
    fun sayHello() {
        println("Hello, my name is $name and I am $age years old.")
    }
}

2.2.2.7. 枚举类(enum class):定义了一组有限的值,枚举值之间用逗号分隔。可以使用 when 表达式来匹配枚举值。

enum class Person {
    var name: String = ""
    var age: Int = 0
    
    fun sayHello() {
        println("Hello, my name is $name and I am $age years old.")
    }
}

2.3 函数定义

Kotlin 中的函数可以定义在文件中,也可以定义在类中。函数定义的语法如下:

2.3.1 定义

// 定义一个函数
fun sum(a: Int, b: Int): Int {
    return a + b
}

// 调用函数
val result = sum(1, 2)

其中,functionName 表示函数名,arg1、arg2 等表示函数参数,Type1、Type2 表示参数的类型,ReturnType 表示函数返回值的类型。

2.3.2 参数

2.3.2.1 默认参数

fun add(a: Int, b: Int): Int {
    return a + b
}

在函数定义时,可以给参数指定默认值,当调用该函数时,如果省略了该参数,则使用默认值。

2.3.2.2 可变参数

fun sum(vararg numbers: Int): Int {
    var total = 0
    for (number in numbers) {
        total += number
    }
    return total
}

println(sum(1, 2, 3, 4, 5)) // 输出:15

在函数定义时,可以使用 vararg 关键字来指定一个参数为可变参数,表示该参数可以接受任意数量的值。例如:

2.3.2.3 高阶参数

fun add(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
    return operation(a, b)
}

val result = add(2, 3) { a, b -> a + b }
println(result) // 输出:5

在 Kotlin 中,可以将函数作为参数传递给另一个函数,或者从函数中返回另一个函数。这种函数被称为高阶函数

在这个例子中,add() 函数接受三个参数:a、b 和一个函数 operation。在调用 add() 函数时,传递了一个 lambda 表达式作为 operation 参数,表示将 a 和 b 相加。最终返回结果 5。

2.4 流程控制语句

// if 表达式
val result = if (a > b) a else b

// when 表达式
when (x) {
    1 -> print("x is 1")
    2 -> print("x is 2")
    else -> print("x is neither 1 nor 2")
}

三、空安全

Kotlin 对空指针异常进行了处理,它引入了可空类型和非空类型的概念,使得编写代码时更加安全和可靠。

  1. 可空类型(nullable type):在类型后面加上 ? 表示该类型可以为 null
  2. 安全调用操作符(safe call operator):在对象调用方法或属性时,使用 ?. 来避免出现 NPE。
  3. Elvis 操作符(elvis operator):使用 ?: 表示如果对象为 null,则返回默认值。
  4. 非空断言操作符(not-null assertion operator):在对象后面加上 !! 表示非空断言,即对象不为 null,否则会抛出 NPE 异常。
// 可空类型
var str: String? = null

// 安全调用操作符
val length = str?.length

// Elvis 操作符
val length2 = str?.length ?: 0

// 非空断言操作符
val length3 = str!!.length

使用空安全特性可以让代码更加健壮和安全。但需要注意的是,过度使用 !! 非空断言操作符可能会导致代码出现运行时异常,因此应该谨慎使用。

四、扩展函数与高阶函数

4.1 扩展函数

在 Kotlin 中,扩展函数(Extension Functions)是一种非常方便的语言特性,可以为已有的类添加新的方法,而无需继承或修改该类的源代码。

扩展函数使用 fun 关键字来定义,其第一个参数为接收者类型(Receiver Type),即被扩展的类,使用 . 语法来调用。

// 为 String 类添加一个扩展函数
fun String.customFunction(): String {
    return "This is a custom function for String"
}

// 使用扩展函数
val str = "Hello World"
val result = str.customFunction()

通过扩展函数,我们可以为标准库中的类(如 StringListInt 等)添加自定义的方法,或为我们自己定义的类添加新的方法,从而让代码更加简洁、易读和可维护。但需要注意的是,扩展函数不能访问被扩展类的私有或受保护的成员。

4.2 高阶函数

可以接受函数作为参数或者返回函数作为结果的函数。这种语言特性使得编写具有抽象能力的代码变得更加容易。

// 定义一个接受函数参数的高阶函数
fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {
    return operation(x, y)
}

// 定义一个函数类型的变量
val add: (Int, Int) -> Int = { a, b -> a + b }

// 调用高阶函数,并传入函数类型的参数
val result = calculate(10, 5, add)

在上面的代码中,我们定义了一个接受函数参数的高阶函数 calculate,该函数接受两个整数和一个函数类型的参数 operation,并返回 operation 的计算结果。我们又定义了一个函数类型的变量 add,其接受两个整数并返回它们的和。最后,我们调用 calculate 函数并传入 add 作为参数。

高阶函数在函数式编程中应用广泛,可以大大提高代码的抽象能力和可读性,使得代码更加灵活、可重用和可维护。

4.3 常用的一些函数

4.3.1. let

函数是一种作用域函数,它可以在一个对象上执行代码块,然后返回结果。通常用于在代码块中执行一些操作,例如对对象进行非空判断、类型转换、计算等操作。let 函数的用法如下:

object?.let {
    // 在 object 不为空的情况下执行以下代码块
    // 可以使用 it 代替 object,它是执行代码块的对象
}

val str: String? = "Hello, Kotlin"
str?.let { s ->
    // s 不为 null,可以在这里对 s 进行操作
    print(s)
}

4.3.2. apply

函数是一种构建函数,它可以在一个对象上执行一系列操作,并返回该对象本身。通常用于在对象构建过程中执行一些操作,例如初始化属性、设置默认值等操作。apply 函数的用法如下:

object.apply {
    // 在 object 上执行以下代码块
    // 可以使用 this 代替 object,它是执行代码块的对象
}.run {
    // apply 函数返回 object,可以继续在其上执行其他操作
}

val person = Person().apply {
    name = "Alice"
    age = 30
    address = "123 Main St"
}

需要注意的是,letapply 函数的区别在于它们的返回值,let 函数返回执行代码块的结果,而 apply 函数返回执行代码块的对象本身。

4.3.3. run

是 Kotlin 标准库中的一种作用域函数,它可以在一个对象上执行代码块,并返回执行结果。常用于在代码块中对对象进行一些操作,例如对对象进行非空判断、类型转换、计算等操作。使用 run 函数可以使代码更加简洁、清晰、安全。

object.run {
    // 在 object 上执行以下代码块
    // 可以使用 this 代替 object,它是执行代码块的对象
    // 返回执行结果
}

val str: String? = "Hello, Kotlin"
val result = str?.run {
    // this 不为 null,可以在这里对 this 进行操作
    length
}
// result = 13

在上面的示例中,我们使用 run 函数对一个可能为 null 的字符串进行非空判断,并在非空的情况下获取该字符串的长度,最后返回执行结果。

需要注意的是,run 函数与 let 函数类似,都可以帮助开发者编写更加简洁、清晰、安全的代码,同时也可以提高代码的可读性和可维护性。不同之处在于,run 函数可以直接使用 this 访问对象,在代码块中可以省略掉对对象的引用。

4.3.5. with

with 函数是 Kotlin 标准库提供的一个函数,它可以在不使用对象名的情况下,对某个对象进行一系列操作。with 函数的签名如下:

fun <T, R> with(receiver: T, block: T.() -> R): R

其中,receiver 参数表示需要进行操作的对象,block 参数是一个函数类型的参数,表示对 receiver 进行操作的代码块。block 函数类型的接收者是 T,即 receiver 对象本身。

用法如下

with(receiver) {
    // 对 receiver 进行一系列操作
}

在代码块内,可以直接使用 receiver 对象的属性和方法,而无需使用对象名进行调用。代码块执行完毕后,会返回最后一个表达式的值。

// 定义一个 Person 类
class Person(var name: String, var age: Int)

// 创建一个 Person 对象
val person = Person("Alice", 25)

// 使用 with 函数修改 Person 对象的属性
with(person) {
    name = "Bob"
    age = 30
}

// 打印修改后的属性值
println("Name: ${person.name}, Age: ${person.age}") // 输出:Name: Bob, Age: 30

在上面的代码中,我们使用 with 函数对 person 对象进行了一系列操作,包括修改 nameage 属性的值。最后,我们打印了修改后的属性值。

总的来说,with 函数可以简化对某个对象的多次操作,并提高代码的可读性。但需要注意的是,with 函数只是提供了一种简化代码的方式,并不会对代码的性能产生显著的影响。

4.3.4 其它函数

  1. also 函数:在一个对象上执行一系列操作,返回该对象本身。
  2. takeIf 函数:根据条件判断是否使用该对象,并返回结果。
  3. takeUnless 函数:根据条件判断是否不使用该对象,并返回结果。
  4. repeat 函数:重复执行代码块指定次数。

其它

  1. Kotlin 官方文档:kotlinlang.org/docs/home.h…
  2. Kotlin Playground:play.kotlinlang.org/
  3. Kotlin 专栏:www.jianshu.com/c/5b6c58d6f…
  4. Kotlin 中文网:www.kotlincn.net/
  5. Kotlin 实战:www.kotliner.cn/
  6. Kotlin 手册:www.kotlincn.net/docs/refere…
  7. Kotlin 学习笔记:www.yiibai.com/kotlin/
  8. Kotlin 知识库:www.kotlincn.net/kotlin-know…
  9. Kotlin 官方博客:blog.jetbrains.com/kotlin/
  10. Kotlin 语言教程:www.runoob.com/kotlin/kotl…