说明
本文知识源:
  1. kotlin 官方文档
  2. 中文文档
  3. 强烈建议直接官方文档,中文文档应该是机器翻译的,很多不好理解的地方。

一、数据结构

数据类型

  1. 类型和Java基本一致
  2. 不声明的话,数字量根据数值大小默认为 Int 或者 Long
  3. 无符号数,数字后面加u表示
  4. 没有基本类型,包装类型之分
数据转换
  1. 不能隐式转换,如int -> long,float -> double,都必须显示声明
  2. Char 不能直接当数字, ‘a’.toInt()
字符串

更加好用的字符串

  1. 一段字符串
var str = ''' 
123
456
789
'''
  1. 换行字符串
    |
    |
  2. 字符串模板
var str = "123"
println("$str.length:{$str.length}")

二、控制流

三元运算
var a = if (a > 10) 10 else 0;
when 语句
when (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("none of the above")
}
for 循环

withIndex() 给出索引值

for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}
return返回

直接返回到最外层函数

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach {
        if (it == 3) return // 非局部直接返回到 foo() 的调用者
        print(it)
    }
    println("this point is unreachable")
}

内部函数返回return语句

fun foo() {
    listOf(1, 2, 3, 4, 5).forEach myfun{
        if (it == 3) return@myfun // 非局部直接返回到 foo() 的调用者
        print(it)
    }
    println("this point is reachable")
}

三、类与对象

构造函数
  1. 主构造函数:类名constructor 次级构造函数:类中方法
class Person constructor(name:String){
    
    constructor(fullName:String):this(fullName.toUpperCase()){
        println('次级构造函数')
    }
    
}
  1. 在 Kotlin 中的一个类可以有一个主构造函数以及一个或多个次构造函数。主构造函数是类头的一部分:它跟在类名后。
  2. 使用init初始化代码块
  3. init块中的代码实际上会成为主构造函数的一部分
  4. 主构造函数会作为次构造函数的第一条语句(因为委托的关系)

面向对象

类相关
  1. 超类 Any
  2. open 代表可以被覆写,被继承,override 显示代表覆写,继承
  3. 在一个内部类中访问外部类的超类,可以通过由外部类名限定的 super 关键字来实现:super@Outer
  4. 多继承时,为了表示采用从哪个超类型继承的实现,使用super<超类型名>,如 super
  5. 伴生对象: 用来声明类似Java的静态成员或方法
成员属性
  1. 标准声明
var stringRepresentation: String
    get() = this.toString()
    set(value) {
        setDataFromString(value) // 解析字符串并赋值给其他属性
    }
  1. get 或 set
var setterVisibility: String = "abc"
    private set // 此 setter 是私有的并且有默认实现

var setterWithAnnotation: Any? = null
    @Inject set // 用 Inject 注解此 setter
  1. set方法内,不能直接调用该字段,否则会栈溢出,使用幕后字段field,来代表改成员变量
var counter = 0 // 注意:这个初始器直接为幕后字段赋值
    set(value) {
        if (value >= 0) field = value
    }
  1. 编译期常量 const
    object 或 companion object 类中的属性,基本类型
  2. 延迟属性 lateinit
  3. 外部类不能访问内部类的 private 成员。

接口

  1. 接口无法保存状态,它可以有属性但必须声明为抽象或提供访问器实现
  2. 接口的方法可以仅仅只声明,也可有实现

扩展函数

  1. 相当于静态方法
  2. 可用this指代方法的调用者
  3. 调用时,根据调用者的声明类型来调用指定的函数
  4. 如果一个类定义有一个成员函数与一个扩展函数,而这两个函数其他都相同,这种情况总是取成员函数

扩展属性

val <T> List<T>.lastIndex: Int
    get() = size - 1

特殊类

// 数据类:用来存储数据,编译器自动编写 toString equals hashCode 方法
data Person(var name:String)
// 密封类:当一个值为有限几种的类型、而不能有任何其他类型。相当于是增强型枚举
sealed class Expr
// 匿名内部类:
window.addMouseListener(object : MouseAdapter() {

    override fun mouseClicked(e: MouseEvent) { …… }

    override fun mouseEntered(e: MouseEvent) { …… }
})
// 枚举类
enum class RGB { RED, GREEN, BLUE }
// 枚举类默认的属性:名称及索引
//val name: String
//val ordinal: Int

泛型

设计原因:Java 泛型的缺陷,Java泛型是不型变,传递过程中会出现问题。

List a --> List b 没有继承关系

声明处型变 out

生成返回的类型,而从不被消费,一般用于函数返回值的类型设定

类型参数逆变 in

只可以被消费而不可以被生产,一般用于函数参数

特别的泛型*

对于声明的类Function <in T, out U>,有如下解释:

  1. Function<*, String> 表示 Function<in Nothing, String>;
  2. Function<Int, *> 表示 Function<Int, out Any?>;
  3. Function<*, *> 表示 Function<in Nothing, out Any?>
泛型声明 reified
inline fun <reified T> TreeNode.findParentOfType(): T? {
    var p = parent
    while (p != null && p !is T) {
        p = p.parent
    }
    return p as T?
}
treeNode.findParentOfType<MyTreeNode>()

对象

typealias 类型别名

内联类

  1. 主构造器仅有一个基本属性的类
  2. 内联类不能含有 init 代码块
  3. 内联类不能含有幕后字段
  4. 无法继承其他类,能实现接口

委托

类的委托:
  • 用来帮助实现继承中一些方便的覆写。但是本身的覆写会优先于委托的覆写
interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}

class Derived(b: Base) : Base by b
委托属性

用处:

  • 延迟加载
  • 可观察属性
  • 属性映射
// 延迟属性
val lazyValue: String by lazy {
    println("computed!")
    "Hello"
}
// 可观察属性
var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
// 属性映射
class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}
val user = User(mapOf(
    "name" to "John Doe",
    "age"  to 25
))

局部变量也可以使用委托:

fun example(computeFoo: () -> Foo) {
    val memoizedFoo by lazy(computeFoo)

    if (someCondition && memoizedFoo.isValid()) {
        memoizedFoo.doSomething()
    }
}

四、函数

相关关键字
  1. 可变参数:vararg
  2. 中缀函数: infix
  3. 尾递归函数: tailrec
匿名函数
fun(x: Int, y: Int): Int = x + y
带有接收者的函数
val sum: Int.(Int) -> Int = { other -> plus(other) }
// 很强大的应用 DSL风格的函数调用
class HTML {
    fun body() { …… }
}

fun html(init: HTML.() -> Unit): HTML {
    val html = HTML()  // 创建接收者对象
    html.init()        // 将该接收者对象传给该 lambda
    return html
}

html {       // 带接收者的 lambda 由此开始
    body()   // 调用该接收者对象的一个方法
}
内联函数 inline

出现原因:性能考虑,对于函数对象和类的内存分配和虚拟调用会引入运行时间开销

特性:

  1. lambda 表达式传给的函数如果是内联的,则可以局部函数内return
禁用内联 noinline

声明内联函数的函数类型参数,禁止传入的函数是内联的

crossinline 函数参数禁止在局部返回

声明内联函数的函数类型参数,禁止传入的函数内部局部返回return