第十一天笔记

image-20240716082517092

Kotlin 讲义总结

image-20240716084448927

image-20240716092435890

1. 包的定义与导入

1) 包的声明

  • 包的声明在代码文件的开头,格式为 package 包名
  • 包名是工程根目录下的子目录,如 com.jetbrains
  • 在根目录下创建 com.jetbrains 包,并在此包下创建 Demo1.kt 文件,文件第一行声明包名:package com.jetbrains

2) 包的导入

  • 包的导入格式为 import 资源全名
  • Demo1.kt 文件中添加 demo1 函数,函数全名为 com.jetbrains.demo1
  • Main.kt 文件中导入并使用 com.jetbrains.demo1
import com.jetbrains.demo1

fun main(args: Array<String>) {
    demo1()
}
  • Kotlin 默认导入一些包,无需手动导入:
    • kotlin.*
    • kotlin.annotation.*
    • kotlin.collections.*
    • kotlin.comparisons.*
    • kotlin.io.*
    • kotlin.ranges.*
    • kotlin.sequences.*
    • kotlin.text.*

2. 程序入口

  • 每个 Kotlin 程序都有一个执行入口,在每个 Kotlin 文件中,main 函数是执行入口:
fun main() {
    println("Hello world!")
}
  • main 函数可以接收可变数量的字符串参数:
fun main(args: Array<String>) {
    println(args.contentToString())
}
  • 在命令行中运行程序并传递参数:
$ kotlin MainKt a b c
  • 在 IDEA 中直接运行时可以在输出目录下找到编译后的 MainKt.class 文件,并手动执行。

3. 标准输入输出

标准输出

  • print 打印内容到标准输出,不换行。
  • println 打印内容到标准输出并换行。
print("Hello, ")
print("World!")
println("Hello, World!")
println(123)
println(3.14)

image-20240716094027674

标准输入

image-20240716092715937

  • readln() 读取一行输入,返回 String 类型。
val value = readln()
  • 使用 toInt() 等方法将输入转换为其他基本数据类型:
val intValue = readln().toInt()
val longValue = readln().toLong()
val doubleValue = readln().toDouble()
  • 如果使用 Kotlin 1.6 之前的版本,需使用 readLine()!!
val value = readLine()!!
  • 使用 split 方法读取多个输入数据,例如空格分割:
val (a, b) = readln().split(" ")

4. 函数

函数定义

  • 使用关键字 fun 定义函数:
fun sum(a: Int, b: Int): Int {
    return a + b
}
  • Kotlin 支持函数的表达式形式,返回值可以自动推断:
fun sum(a: Int, b: Int) = a + b

返回 Unit 类型

  • 如果函数不返回任何有意义的值,可以用 Unit 关键字:
fun printSum(a: Int, b: Int): Unit {
    println("$a$b 的和为: ${a + b}")
}
  • Unit 类型可以省略:
fun printSum(a: Int, b: Int) {
    println("$a$b 的和为: ${a + b}")
}

参数默认值

  • 函数参数可以有默认值:
fun sum(a: Int, b: Int = 0): Int {
    return a + b
}

4. 函数

函数调用

  • 可以在其他函数中调用函数,例如在 main 函数中调用 sum 函数:

    fun main() {
        val res = sum(1, 2)
        println(res)
    }
    
  • 区别不同函数可以使用函数名和参数列表来确定:

    fun sum(a: Int): Int { return a }
    fun sum(a: Int, b: Int): Int { return a + b }
    fun printSum(a: Int, b: Int): Unit { println(a + b) }
    
  • 函数调用示例:

    fun main() {
        val a = sum(1)
        println(a)
        val b = sum(1, 2)
        println(b)
        printSum(1, 2)
    }
    
  • 函数的调用会涉及到入栈、出栈操作,遵循先入后出的原则。

5. 变量

使用 val 关键字定义只读变量

  • 只读变量赋值后不可更改,但可以延迟初始化:

    fun main() {
        val a: Int = 1  // 立即赋值
        val b = 2       // 自动推断类型
        val c: Int      // 提供初始值前,先声明类型
        c = 3           // 延后初始化
        println("a = $a, b = $b, c = $c")
    }
    

使用 var 关键字定义可变变量

  • 可变变量可以重新赋值:

    fun main() {
        var a = 1
        a = 2
        println("a = $a")
    }
    
  • val 等价于 Java 的 private final 变量,var 等价于 private 变量。

  • 官方推荐优先使用 val 定义变量,避免副作用。

6. 基本数据类型

数值类型 (Number)

  • Kotlin 的数值类型分为两种:整数类型 (Integer types) 和浮点类型 (Floating-point types)。
类型 大小 最小值 最大值
Byte 8bits(1Byte) -128 127
Short 16bits(2Byte) -32768 32767
Int 32bits(4Byte) -2,147,483,648 (-2^31) 2,147,483,647 (2^31-1)
Long 64bits(8Byte) -9,223,372,036,854,775,808 (-2^63) 9,223,372,036,854,775,807 (2^63-1)

浮点类型

Kotlin 提供两种浮点类型:FloatDouble

类型 大小 有效位 指数位 小数位数
Float 32bits(4Byte) 24 8 6-7
Double 64bits(8Byte) 53 11 15-16

这些类型都继承自 Number 类。

整数类型

在 Kotlin 中,整数类型的变量初始化时,如果显式指定数据类型,编译器会自动推断:

  • 如果值在 Int 类型范围内,默认为 Int 类型。
  • 如果超过 Int 类型范围,自动转换为 Long 类型。

例如:

val int = 1 // Int类型
val long = 2147483648 // Long类型
println(long is Long) // true

可以通过添加后缀 L 显式指定 Long 类型:

val long = 2147483648L // Long类型

默认情况下,变量类型为 Int,可以通过显式声明指定其他类型:

val byte: Byte = 1 // Byte类型

如果变量值超出类型限制,编译器会报错。

无符号整型

Kotlin 提供了无符号整数类型:

类型 数据范围
UByte 0-255
UShort 0-65535
UInt 0-2^32-1
ULong 0-2^64-1

浮点型变量

初始化浮点型变量时,未显式指定类型默认为 Double。可以通过添加后缀 F 指定为 Float

val double = 1.0 // Double类型
val float = 1.0f // Float类型

如果显式指定浮点类型却赋值为整数,编译器会报错。

JVM 中的数字表示

在 Kotlin 中,== 用于比较数值,=== 用于比较对象引用。

对于可空的数字引用:

val a = 128
val b: Int? = a
val c: Int? = a
println(b == c) // true
println(b === c) // false

当值在 -128 到 127 范围内时,对象引用相同:

val a = 127
val b: Int? = a
val c: Int? = a
println(b === c) // true

类型转换

Kotlin 提供类型转换函数如 toInt(), toDouble() 等,如 sqrt() 函数需要传入浮点型:

val num = 16.toDouble()
println(sqrt(num))

2. 布尔类型

布尔类型的值只有 truefalse 两种,布尔值可以为空。

val a: Boolean = true
val b = false
val c: Boolean? = null
println(a && b) // false
println(a || b) // true

3. 字符类型

与 Java 不同,Kotlin 中字符类型不属于数值类型,不能与数字进行操作。

val a: Char = 'a'
val b = 'b'
val c = '\n'
print(a)
print(c)
print(b)

可以通过 code 属性获取字符的 ASCII 编码。

val a = 'a'
println(a.code) // 97

4. 字符串类型

字符串类型用 "" 表示。

val s: String = "I'm a coder"
println(s)

可以用 in 关键字判断字符是否出现在字符串中,也可以通过该关键字遍历字符串。

val c = 'I'
val s: String = "I'm a coder"
println(c in s) // true
for (c in s) {
    println(c)
}

可以通过 uppercase() 方法将字符串全转换为大写。

println("abcd".uppercase()) // ABCD

可以通过 + 来连接字符串。

val a = "abc"
val b = a + "def"
println(b)

可以通过 """ """ 来使用原始字符串,它不会处理转义符,可以包含换行符和任意字符,可以通过 trimMargin() 去掉前导空格。

val s = """
    #include <iostream>
    using namespace std;
    int main() {
        cout << "Hello, World!"
    }
""".trimMargin()
println(s)

字符串模板:

val i = 1
println("i = $i") // i = 1
val s = "abc"
println("s.length = ${s.length}") // s.length = 3

5. 数组类型

在 Kotlin 中,数组由 Array 类表示。

数组的定义可以通过 arrayOf() 或者 Array 构造函数方式。

val a: Array<String> = arrayOf("a", "b", "c")
val b: Array<Int> = arrayOf(1, 2, 3)

可以通过 forEach() 方法遍历数组。

a.forEach { println(it) }

数组类有 size 属性,可以通过它得到数组的长度。

6. 任意类型

Kotlin 中的 Any 类是所有非空类型的超类,与 Java 中的 Object 类相似。

image-20240716100219069

7. 类型的检查和强制转换

  • 类型检查:使用 is 关键字进行类型检查。

    val s = "Hello"
    println(s is String) // true
    
  • 不安全类型转换:使用 as 关键字进行强制转换,如果转换失败会抛出异常。

    val y = null
    val x: String = y as String // 抛出异常
    
  • 安全类型转换:使用 as? 关键字进行安全类型转换,转换失败时返回 null 而不会抛出异常。

    val y = null
    val x: String? = y as? String
    println(x) // null
    

8. 条件判断、循环语句

if 判断条件
  • 基本形式

    val a = 1
    val b = 2
    var max = a
    if (a > b) {
        max = a
    } else {
        max = b
    }
    
  • 表达式形式

    val a = 1
    val b = 2
    val max = if (a > b) a else b
    
  • 块中的表达式

    val a = 1
    val b = 2
    val max = if (a > b) {
        println(a)
        a
    } else {
        println(b)
        b
    }
    
  • in 关键字搭配使用

    if (1 in 1..10) {
        println("Yes")
    } else {
        println("No")
    }
    
when 表达式
  • 基本用法:类似于 switch,但功能更强大。

    val x: Int = 1
    when (x) {
        1 -> println(1)
        2 -> println(2)
        else -> println("No condition matched")
    }
    
  • 多值匹配

    when (x) {
        0, 1 -> println("x is 0 or 1")
        else -> println("x is neither 0 nor 1")
    }
    

when 表达式

  • 简单匹配

    val x: Int = 1
    when (x) {
        1, 2 -> println("1 or 2")
        else -> println("No conditions matched")
    }
    
  • 传入函数,匹配函数的返回值

    fun getNum(x: Int) = x * 2
    
    fun main() {
        when (getNum(2)) {
            2 -> println(2)
            4 -> println(4)
        }
    }
    
  • 使用 isin 关键字

    fun getNum(x: Int) = x * 2
    
    fun main() {
        when (getNum(2)) {
            in 1..2 -> println("1..2")
        }
    }
    

循环语句

  • for 循环

    for (i in 1..10) {
        println(i) // 1 到 10
    }
    for (i in 0..6 step 2) {
        println(i) // 0, 2, 4, 6
    }
    for (i in 6 downTo 0 step 2) {
        println(i) // 6, 4, 2, 0
    }
    
  • 遍历 Array 对象

    val array: Array<Int> = arrayOf(1, 2, 3, 4)
    for (i in array) {
        println(i)
    }
    
  • 通过索引遍历

    val array: Array<Int> = arrayOf(1, 2, 3, 4)
    for (i in array.indices) {
        println(array[i])
    }
    
  • 使用 withIndex

    val array: Array<Int> = arrayOf(1, 2, 3, 4)
    for ((index, value) in array.withIndex()) {
        println("index: $index, value: $value")
    }
    
  • 使用 breakcontinue

    for (i in array.withIndex()) {
        if (i == 2) break // 终止循环
        if (i == 1) continue // 跳过本次循环
    }
    

while 循环

  • 基本用法

    var x = 3
    while (x > 0) {
        println(x)
        x--
    }
    
    var y = 3
    do {
        println(y)
        y--
    } while (y > 0)
    

面向对象

image-20240716101759989

image-20240716102348657

image-20240716102600960

image-20240716102649476

image-20240716103457391

image-20240716103834472

image-20240716104235074

image-20240716104502492

image-20240716104954623

image-20240716105133039

image-20240716105257218

image-20240716105303291

image-20240716105353449

特殊语法糖

image-20240716153119923

image-20240716153551289

image-20240716153759070

image-20240716154145691

image-20240716154340733

image-20240716154531855

image-20240716154649578

image-20240716155650822

image-20240716155859063

image-20240716160352888

image-20240716160454861

image-20240716161102578

class DetailActivity : AppCompatActivity() {
    companion object {
        const val EXTRA_POSITION = "position"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_detail)

        val pos = intent.getIntExtra(EXTRA_POSITION, -1)

        val textView = findViewById<TextView>(R.id.text)
        val imageView = findViewById<ImageView>(R.id.image)
        val likeView = findViewById<View>(R.id.like)

        when (val item = ItemDataManager.items[pos]) {
            is Item.TextItem -> {
                textView.text = item.text
                textView.visibility = View.VISIBLE
                imageView.visibility = View.GONE
                likeView.run {
                    isSelected = item.like
                    setOnClickListener {
                        item.like = !item.like
                        isSelected = item.like
                    }
                }
            }

            is Item.ImageItem -> {
                imageView.setImageResource(item.imageResId)
                imageView.visibility = View.VISIBLE
                textView.visibility = View.GONE
                likeView.run {
                    isSelected = item.like
                    setOnClickListener {
                        item.like = !item.like
                        isSelected = item.like
                    }
                }
            }
        }
    }
}
class MyAdapter(
    private val items: List<Item>,
    private val onClick: (Int) -> Unit
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    class TextViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val textView: TextView? = itemView.findViewById(R.id.text)
        val likeView: View? = itemView.findViewById(R.id.like)
    }

    class ImageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val imageView: ImageView? = itemView.findViewById(R.id.image)
        val likeView: View? = itemView.findViewById(R.id.like)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        return when (viewType) {
            TEXT_TYPE -> {
                val view =
                    LayoutInflater.from(parent.context).inflate(R.layout.item_text, parent, false)
                TextViewHolder(view)
            }

            IMAGE_TYPE -> {
                val view =
                    LayoutInflater.from(parent.context).inflate(R.layout.item_image, parent, false)
                ImageViewHolder(view)
            }

            else -> {
                val view =
                    LayoutInflater.from(parent.context).inflate(R.layout.item_text, parent, false)
                TextViewHolder(view)
            }
        }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        holder.itemView.setOnClickListener {
            onClick(position)
        }
        when (val item = items[position]) {
            is Item.TextItem -> {
                val textHolder = holder as TextViewHolder
                textHolder.textView?.text = item.text
                textHolder.likeView?.run {
                    isSelected = item.like
                    setOnClickListener {
                        item.like = !item.like
                        isSelected = item.like
                    }
                }
            }

            is Item.ImageItem -> {
                val imageHolder = holder as ImageViewHolder
                imageHolder.imageView?.setImageResource(item.imageResId)
                imageHolder.likeView?.run {
                    isSelected = item.like
                    setOnClickListener {
                        item.like = !item.like
                        isSelected = item.like
                    }
                }
            }
        }
    }

    override fun getItemCount() = items.size

    override fun getItemViewType(position: Int) = when (items[position]) {
        is Item.TextItem -> TEXT_TYPE
        is Item.ImageItem -> IMAGE_TYPE
    }

    companion object {
        private const val TEXT_TYPE = 1
        private const val IMAGE_TYPE = 2
    }
}