移动端成长日记

记录成长的点滴

Kotlin基础

类型

  • kotlin没有类型的隐式转换,必须显式转换
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    val b: Byte = 1 // OK, 字面值会静态检测
    // val i: Int = b // 错误
    val i1: Int = b.toInt() // 使用toInt方法显示转换类型
    /* toByte(): Byte
    toShort(): Short
    toInt(): Int
    toLong(): Long
    toFloat(): Float
    toDouble(): Double */
    // 这种情况可以隐式推导
    val l = 1L + 3 // Long + Int => Long
  • 整数类型:Byte(8), Short(16), Int(32), Long(64)
  • 浮点类型:Float(32)(可以加f或者F后缀指定), Double(64)
  • 数字字面值常量:十六进制0xFF,二进制0b01
  • 当初始化一个没有显式指定类型的变量时,编译器会自动推断为足以表示该值的最小类型。
    1
    2
    3
    var one = 1L  // 可以显式指定后缀L表示该类型为long
    // 使用下划线表示数字常量更易读
    val oneMillion = 1_000_000
  • JVM平台的数字表示为原生类型int, double等,例外情况是当创建可空数字引用如Int? 或者使用泛型时。在这些场景中,数字会装箱为Java类 IntegerDouble等,这样就可以使用一些方法如valueOf()
  • 默认情况下kotlin中的所有声明都默认是非空的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    fun main() {
    // var是可变的,而val是不可变的(类似于const)
    val a: Int = 100
    // Int后面带问号表示类型为可空的
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a

    val b: Int = 10000
    val boxedB: Int? = b
    val anotherBoxedB: Int? = b

    // "==="用于比较两个对象的引用(JVM没有指针的概念,实际上操作的是引用(同cpp))
    // 可空引用的不同的原因在于JVM对-128到127的整数(Integer)应用了内存优化
    println(boxedA === anotherBoxedA) // true
    println(boxedB === anotherBoxedB) // false
    // 虽然引用不同但是它们值相等
    println(boxedB == anotherBoxedB) // true
    }
  • 整数(非浮点)除法会自动向下取整
  • 位运算
    1
    2
    3
    4
    5
    6
    7
    shl(bits) – 有符号左移
    shr(bits) – 有符号右移
    ushr(bits) – 无符号右移
    and(bits) – 位与
    or(bits) – 位或
    xor(bits) – 位异或
    inv() – 位非
  • 区间实例以及区间检测:a..b, x in a..b, x !in a..b

控制流程

  • when语句
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    when (x) {  // 类似于switch
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> { // 类似于defualt
    print("x is neither 1 nor 2")
    }
    }
    // 也可以这么写
    when (x) {
    // 多种情况
    0, 1 -> print("x == 0 or x == 1")
    else -> print("otherwise")
    }
    // 可以用任意表达式,不只是常量,来作为分支条件
    when (x) {
    s.toInt() -> print("s encodes x")
    in 1..10 -> print("x is in the range")
    in validNumbers -> print("x is valid")
    !in 10..20 -> print("x is outside the range")
    else -> print("s does not encode x")
    }
    // 可以使用`is`来检测是否为特定类型的值
    fun hasPrefix(x: Any) = when(x) {
    is String -> x.startsWith("prefix")
    else -> false
    }
  • for范围语句
    1
    2
    3
    for (item: Int in ints) {
    // ……
    }
    • 提供了迭代器对象
      有一个成员函数或者扩展函数iterator()返回Iterator<>
      有一个成员函数或者扩展函数next()
      有一个成员函数或者扩展函数hasNext()返回Boolean
    • 若要在数字范围迭代,则使用区间表达式,如:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      fun main() {
      // arrayOf创建数组
      val array = arrayOf("a", "b", "c")
      // indices 是数组类型的一个属性,用于获取数组的索引范围。
      // i不需要显式定义
      for (i in array.indices) {
      println(array[i])
      }
      }
      // 若想反向迭代,则使用downTo
      for (i in 4 downTo 1 step 1) print(i)
  • 返回,支持label
    1
    2
    3
    4
    5
    6
    // lambda表达式写法,对于list中的每一个元素,都执行后面的语句
    // 其中后跟@的一串字符为label,可以进行对应的跳转
    listOf(1, 2, 3, 4, 5).forEach lit@{
    if (it == 3) return@lit // 局部返回到该 lambda 表达式的调用者——forEach 循环
    print(it)
    }
    • 当要返一个回值的时候,解析器优先选用标签限定的返回
      1
      return@a 1
  • 异常,Kotlin 中所有异常类继承自Throwable类。 每个异常都有消息、堆栈回溯信息以及可选的原因。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    fun main() {
    throw Exception("Hi There!")
    try {
    // 一些代码
    } catch (e: SomeException) {
    // 处理程序
    } finally {
    // 可选的 finally 块
    }
    }

包与导入

  • 源文件所有内容(无论是类还是函数)都包含在该包内。源文件通常以包声明开头
  • kotlin中会默认导入
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    kotlin.*
    kotlin.annotation.*
    kotlin.collections.*
    kotlin.comparisons.*
    kotlin.io.*
    kotlin.ranges.*
    kotlin.sequences.*
    kotlin.text.*
    // 如果是JVM会额外导入
    java.lang.*
    kotlin.jvm.*
  • 如果出现包名冲突,可以用as关键字来消除歧义
    1
    2
    import org.example.Message // Message 可访问
    import org.test.Message as TestMessage // TestMessage 代表“org.test.Message”
  • 关键字import除了导入类以外还可以,导入顶层函数及属性、在对象声明中声明的函数和属性、枚举常量
  • 顶层函数及属性(类似于全局函数和变量)

函数

  • 如果函数没有返回类型,那么默认返回类型就是Unit,如
    1
    2
    3
    4
    5
    6
    7
    8
    // 函数前需要加func关键字
    fun printMessage(message: String): Unit {
    println(message)
    }
    // 等价的
    fun printMessage(message: String) {
    println(message)
    }
  • lambda表达式
    1
    fun maxOf(a: Int, b: Int) = if (a > b) a else b
  • 被替换的方法中某些参数的类型以问号 ? 为后缀。这表示传递给这些参数的实际参数可以为 null

    变量

  • 自动推导,类似于cpp的auto
    1
    2
    3
    4
    val a: Int = 1  // 立即赋值
    val b = 2 // 自动推断出 `Int` 类型
    val c: Int // 如果没有初始值类型不能省略
    c = 3 // 明确赋值