万籁俱寂,万字将成。
刘耀文
Stay hungry. Stay foolish.
© 2024-2026
Powered by Mix Space&
余白 / Yohaku
.
正在被0人看爆
关于
关于本站关于我
更多
时间线友链
联系
写留言发邮件 ↗
刘耀文
Stay hungry. Stay foolish.
链接
关于本站·关于我·时间线·友链·写留言·发邮件
© 2024-2026 Powered by Mix Space&
余白 / Yohaku
.
正在被0人看爆
赣ICP备2024031666号
RSS 订阅·站点地图·
··|
RSS 订阅·站点地图·|··|赣ICP备2024031666号
稍候片刻,月出文自明。

Kotlin 备忘录

13
AI·GEN

关键洞察

Kotlin 备忘录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • 基本语法

    核心概念

    • 包和导入 (Packages and Imports):将代码组织成命名空间。
    • main 函数:Kotlin 应用程序的入口点。可以接受一个字符串数组作为参数。
    • 打印到标准输出 (Printing to Standard Output):print() 和 println() 用于显示信息。println() 会在输出后添加换行符。
    • 从标准输入读取 (Reading from Standard Input):readln() 从控制台读取一行文本。
    • 函数 (Functions):使用 fun 关键字定义。可以有表达式体和推断返回类型。Unit 返回类型用于不返回有意义值的函数。
    • 变量 (Variables):
      • val:不可变(只读)变量。
      • var:可变变量。
      • 类型推断:Kotlin 通常可以自动确定类型。
    • 类和实例 (Classes and Instances):使用 class 关键字定义。属性可以在声明或主体中。继承使用冒号 :。类默认是 final 的;使用 open 允许继承。
    • 注释 (Comments):单行 (//) 和多行 (/* ... */)。多行注释可以嵌套。
    • 字符串模板 (String Templates):使用 $variable 或 ${expression} 将变量和表达式直接嵌入到字符串中。
    • 条件表达式 (Conditional Expressions):if 语句。if 也可以用作表达式。
    • 循环 (Loops):for 循环(迭代集合或范围)和 while 循环。
    • when 表达式:更强大的 switch 语句。
    • 范围 (Ranges):使用 in 运算符检查值是否在范围内。使用 for 迭代范围。
    • 集合 (Collections):使用 for 迭代。使用 in 检查成员资格。使用 lambda 表达式进行过滤、映射和其他操作。
    • 可空值和空值检查 (Nullable Values and Null Checks):使用 ? 将类型标记为可空。在使用可空值之前检查 null。
    • 类型检查和自动转换 (Type Checks and Automatic Casts):使用 is 运算符检查变量的类型。Kotlin 在 if 块中自动将变量转换为检查的类型。

    常见语法

    创建 DTOs (POJOs/POCOs)

    CodeBlock Loading...
    • data class 自动生成 getters (和 var 的 setters)、equals()、hashCode()、toString()、copy() 以及 componentN() 函数(用于解构)。极大地简化了数据类的创建。

    函数参数的默认值

    CodeBlock Loading...
    • 允许在函数定义中为参数指定默认值。调用函数时,可以省略具有默认值的参数。

    过滤列表

    CodeBlock Loading...
    • 使用 filter 函数和一个 lambda 表达式来创建一个只包含满足特定条件的元素的新列表。it 是 lambda 表达式中单个参数的隐式名称。

    检查集合中元素的存在性

    CodeBlock Loading...
    • 使用 in 和 !in 运算符可以轻松地检查集合中是否存在某个元素。

    字符串插值

    CodeBlock Loading...
    • 使用 $ 符号将变量直接嵌入到字符串中,无需使用字符串连接。

    安全读取标准输入

    CodeBlock Loading...
    • toIntOrNull() 尝试将字符串转换为整数,如果转换失败,则返回 null,避免了 NumberFormatException。

    实例检查

    CodeBlock Loading...
    • 使用 is 运算符检查对象的类型。在 when 表达式中,如果检查成功,则会自动将对象转换为该类型。

    只读列表

    CodeBlock Loading...
    • listOf() 创建一个不可变的列表。

    只读映射

    CodeBlock Loading...
    • mapOf() 创建一个不可变的映射。

    访问映射条目

    CodeBlock Loading...
    • 使用方括号 [] 访问映射中的值。

    遍历映射或一对列表

    CodeBlock Loading...
    • 使用解构声明来遍历映射或 Pair 列表。k 和 v 可以是任何方便的名称。

    遍历范围

    CodeBlock Loading...
    • 使用 .. 创建闭区间,使用 ..< 创建开区间。step 指定步长,downTo 创建倒序范围。

    惰性属性

    CodeBlock Loading...
    • lazy 委托确保属性只在第一次访问时初始化。这可以提高性能,特别是对于计算成本高的属性。

    扩展函数

    CodeBlock Loading...
    • 允许向现有类添加新的函数,而无需继承或修改原始类。

    创建单例

    CodeBlock Loading...
    • object 声明创建一个单例对象。Kotlin 自动处理单例的创建和管理。

    使用内联值类来实现类型安全的值

    CodeBlock Loading...
    • value class (需要 @JvmInline 在 JVM 上) 创建一个包装现有类型的类,提供编译时类型安全。防止错误地将 EmployeeId 传递给需要 CustomerId 的函数。

    实例化一个抽象类

    CodeBlock Loading...
    • 使用匿名对象实例化抽象类,需要实现所有抽象方法。

    如果非空简写

    CodeBlock Loading...
    • 使用安全调用运算符 ?. 如果 files 为 null,则表达式的结果为 null,不会抛出 NullPointerException。

    如果非空则执行,否则执行其他操作的简写

    CodeBlock Loading...
    • 使用 Elvis 运算符 ?: 提供一个默认值,如果左侧表达式为 null,则返回该默认值。run 允许执行一个代码块来计算默认值。

    如果为空则执行语句

    CodeBlock Loading...
    • 使用 Elvis 运算符 ?: 抛出一个异常,如果 values["email"] 为 null。

    获取可能为空集合的第一个项目

    CodeBlock Loading...
    • firstOrNull() 返回集合的第一个元素,如果集合为空,则返回 null。Elvis 运算符提供一个默认值,如果 firstOrNull() 返回 null。

    如果不为 null 则执行

    CodeBlock Loading...
    • let 函数只在值不为 null 时执行。it 是 let 块中该值的隐式名称。

    如果非空则映射可空值

    CodeBlock Loading...
    • 结合了安全调用运算符 ?.、let 函数和 Elvis 运算符 ?:,以简洁地转换可空值,并在值或转换结果为空时提供默认值。

    在 when 语句中返回值

    CodeBlock Loading...
    • when 表达式可以返回值,这使得代码更简洁。

    try-catch 表达式

    CodeBlock Loading...
    • try-catch 块可以返回值,允许您将异常处理逻辑嵌入到表达式中。

    if 表达式

    CodeBlock Loading...
    • if 语句可以返回值,允许您根据条件选择不同的值。

    Builder 风格的使用返回 Unit 的方法

    CodeBlock Loading...
    • apply 函数允许您在对象上调用多个方法,并返回该对象本身。这使得可以以 Builder 风格配置对象。

    单表达式函数

    CodeBlock Loading...
    • 如果函数只包含一个表达式,则可以省略花括号和 return 关键字。

    在对象实例上调用多个方法 (with)

    CodeBlock Loading...
    • with 函数允许您在单个对象上调用多个方法,而无需重复对象名称。

    配置对象的属性 (apply)

    CodeBlock Loading...
    • apply 函数允许您配置对象的属性,并返回该对象本身。这对于配置构造函数中不存在的属性很有用。

    Java 7 的 try-with-resources

    CodeBlock Loading...
    • use 函数确保资源(例如流)在使用后被正确关闭,即使发生异常。

    需要泛型类型信息的通用函数

    CodeBlock Loading...
    • reified 关键字允许您在运行时访问泛型类型信息。这对于需要知道泛型类型的函数很有用。

    交换两个变量

    CodeBlock Loading...
    • 使用 also 函数在赋值之前执行一个操作。在这种情况下,also 函数将 a 的值赋给 b,然后将 b 的值赋给 a。

    数据类型

    类型列表

    • 数字 (Numbers):
      • Byte (8位)
      • Short (16位)
      • Int (32位)
      • Long (64位)
      • Float (32位)
      • Double (64位)
      • 无符号类型: UByte, UShort, UInt, ULong
    • 布尔值 (Booleans):Boolean (true / false)
    • 字符 (Characters):Char (Unicode)
    • 字符串 (Strings):String (不可变)
    • 数组 (Arrays):Array<T> (可变) 特定类型数组: IntArray, ByteArray 等

    特殊类型

    • Any:所有非空类型的超类 (类似 Java 的 Object)
    • Nothing: "永远不会返回" 的类型 (例如, 抛出异常的函数)
    • Unit: "没有有意义的返回值" 的类型 (类似 Java 的 void)

    类型检查/转换

    • 类型检查:is
    • 安全转换:as? (失败返回 null)
    • 不安全转换:as (可能抛出异常)

    关键点

    • Kotlin 没有原始类型 (primitive types)。
    • 类型推断强大, 通常无需显式声明类型。

    示例

    CodeBlock Loading...

    控制语句

    if 表达式

    • if (条件) 结果 else 结果 (必须有 else,除非结果是 Unit)
    • 可返回值,类似三元运算符。
    CodeBlock Loading...

    when 语句/表达式

    • 替代 switch。
    • when (变量) { 值 -> 结果 ... else -> 结果 }
    • 作为表达式必须覆盖所有情况 (或有 else)。
    • 可检查类型 (is),范围 (in)。
    CodeBlock Loading...

    for 循环

    • for (item in 集合) { ... }
    • 迭代集合元素。
    • for (i in 范围) { ... } 迭代数字范围。
    CodeBlock Loading...

    while 循环

    • while (条件) { ... } 先判断后执行。
    • do { ... } while (条件) 先执行后判断 (至少执行一次)。
    CodeBlock Loading...

    控制循环

    • break:跳出循环。
    • continue:跳过本次循环剩余部分。
    CodeBlock Loading...

    跳转表达式

    return

    • 默认从最近的封闭函数或匿名函数返回。

    break

    • 终止最近的封闭循环。

    continue

    • 继续最近的封闭循环的下一次迭代。

    这些表达式的类型是 Nothing。

    标签 (Labels)

    Kotlin 中可以使用标签标记表达式,形式为 identifier@。标签可以用于更精确地控制 break 和 continue 的行为。

    CodeBlock Loading...
    • break@label:跳到标签 label 标记的循环之后的执行点。
    • continue@label:跳到标签 label 标记的循环的下一次迭代。

    返回到标签 (Return to Labels)

    在嵌套函数(函数字面量、局部函数、对象表达式)中,可以使用限定的 return 语句从外部函数返回。主要用于从 Lambda 表达式中返回。

    从 Lambda 表达式返回

    1. 显式标签

      CodeBlock Loading...

      在这个例子中, return@lit 从标记为 lit@ 的 Lambda 表达式返回。

    2. 隐式标签:更简洁,标签名称与 Lambda 表达式传递到的函数名相同。

      CodeBlock Loading...

      在这个例子中, return@forEach 从传递给 forEach 的 Lambda 表达式返回。

    3. 匿名函数:可以使用匿名函数代替 Lambda 表达式。return 语句将从匿名函数本身返回。

      CodeBlock Loading...

      使用匿名函数的效果与 continue 类似.

    模拟 break 行为

    可以使用嵌套的 Lambda 表达式和非局部返回来模拟 break 的行为。

    CodeBlock Loading...

    注意

    当返回一个值时,解析器优先选择带标签的 return 语句,例如 return@a 1 被解析为“在标签 @a 处返回 1”,而不是“返回一个标签为 @a 的表达式 (1)”。

    类和继承

    默认超类 (Default Superclass)

    • Kotlin 中所有类都有一个共同的超类 Any。
    • 如果一个类没有声明超类型,则 Any 是默认的超类。
    CodeBlock Loading...
    • Any 有三个方法:equals()、hashCode() 和 toString()。这些方法对于所有 Kotlin 类都是定义的。

    开放类 (Open Class)

    • 默认情况下,Kotlin 类是 final 的,不能被继承。
    • 要使一个类可继承,用 open 关键字标记它。
    CodeBlock Loading...

    显式超类型 (Explicit Supertype)

    • 要在类头中声明显式超类型,将类型放在冒号之后。
    CodeBlock Loading...
    • 主构造函数 (Primary Constructor):如果派生类具有主构造函数,则必须根据其参数在该主构造函数中初始化基类。
    • 二级构造函数 (Secondary Constructor):如果派生类没有主构造函数,则每个二级构造函数必须使用 super 关键字初始化基类型,或者必须委托给另一个执行此操作的构造函数。不同的二级构造函数可以调用基类型的不同构造函数。
    CodeBlock Loading...

    重写方法 (Overriding Methods)

    • Kotlin 需要对可重写成员和重写使用显式修饰符。
    CodeBlock Loading...
    • override 修饰符对于 Circle.draw() 是必需的。如果缺少它,编译器会报错。
    • 如果一个函数没有 open 修饰符(如 Shape.fill()),则不允许在子类中声明具有相同签名的方法,无论是否带有 override。
    • 当添加到 final 类的成员时,open 修饰符不起作用。
    • 用 override 标记的成员本身是 open 的,因此可以在子类中重写。
    • 要禁止重新重写,使用 final。
    CodeBlock Loading...

    重写属性 (Overriding Properties)

    • 重写机制以与方法相同的方式作用于属性。
    • 在超类上声明然后在派生类上重新声明的属性必须以 override 开头,并且它们必须具有兼容的类型。
    • 每个声明的属性都可以被具有初始化器的属性或具有 get 方法的属性重写。
    CodeBlock Loading...
    • 可以使用 var 属性重写 val 属性,但反之则不行。这是因为 val 属性本质上声明了一个 get 方法,并且将其作为 var 重写还会在派生类中声明一个 set 方法。
    • 可以在主构造函数中使用 override 关键字作为属性声明的一部分。
    CodeBlock Loading...

    派生类初始化顺序 (Derived Class Initialization Order)

    • 在构造派生类的新实例期间,基类初始化是作为第一步完成的(仅在计算基类构造函数的参数之前),这意味着它发生在派生类的初始化逻辑运行之前。
    CodeBlock Loading...
    • 当执行基类构造函数时,尚未初始化在派生类中声明或重写的属性。在基类初始化逻辑中使用这些属性(直接或间接通过另一个重写的开放成员实现)可能会导致不正确的行为或运行时故障。
    • 在设计基类时,应避免在构造函数、属性初始化器或 init 块中使用 open 成员。

    调用超类实现 (Calling the Superclass Implementation)

    • 派生类中的代码可以使用 super 关键字调用其超类函数和属性访问器实现。
    CodeBlock Loading...
    • 在内部类中,访问外部类的超类使用用外部类名限定的 super 关键字:super@Outer。
    CodeBlock Loading...

    重写规则 (Overriding Rules)

    • 在 Kotlin 中,实现继承受以下规则约束:如果一个类从其直接超类继承同一成员的多个实现,则它必须重写此成员并提供自己的实现(可能使用继承的实现之一)。
    • 要表示从中获取继承实现的超类型,请使用 super 并以尖括号括起来的超类型名称限定,例如 super<Base>。
    CodeBlock Loading...
    • 可以从 Rectangle 和 Polygon 继承,但它们都有自己的 draw() 实现,因此需要在 Square 中重写 draw() 并为其提供单独的实现以消除歧义。

    泛型:in, out, where

    泛型类

    • 声明:使用尖括号 <> 声明类型参数。
    CodeBlock Loading...
    • 实例化:提供类型参数,或者让编译器推断。
    CodeBlock Loading...

    变型 (Variance)

    • 不变性 (Invariance):默认情况下,Kotlin 中的泛型类型是不变的。这意味着 List<String> 不是 List<Object> 的子类型。
    • 协变 (Covariance):使用 out 关键字声明协变类型参数。协变类型参数只能出现在 out 位置(只能作为返回值,不能作为参数)。如果类 C 的类型参数 T 声明为 out,C<Base> 可以安全地成为 C<Derived> 的超类型,可以理解为 C 是 T 的生产者,不是 T 的消费者。
    • 逆变 (Contravariance):使用 in 关键字声明逆变类型参数。逆变类型参数只能出现在 in 位置(只能作为参数,不能作为返回值)。可以调用 List<? super String> 上将 String 作为参数的方法,如果调用返回 T 的方法,则会返回一个 Object,而不是 String。
    • 声明点变型 (Declaration-site Variance):在类型参数声明处指定变型(使用 in 和 out 关键字)。
    • 使用点变型 (Use-site Variance):在类型使用处指定变型(使用类型投影)。
    • PECS 原则 (Producer Extends, Consumer Super):
      • 如果对象是生产者(只从中读取数据),则使用 out(对应 Java 中的 extends)。
      • 如果对象是消费者(只向其中写入数据),则使用 in(对应 Java 中的 super)。

    out

    • 只返回 T
    CodeBlock Loading...

    in

    • 只获取 T
    CodeBlock Loading...

    类型投影 (Type Projections)

    • 概念:限制泛型类型的用法,以确保类型安全。
    • out 投影:禁止写入,只允许读取(类似于 Array<? extends Object>)。
    • in 投影:禁止读取,只允许写入(类似于 Array<? super String>)。
    CodeBlock Loading...

    星号投影 (Star-projections)

    • 概念:表示对类型参数一无所知,但仍要以安全的方式使用它。
    • Foo<out T : TUpper> 等价于 Foo<out TUpper>(可以安全地读取 TUpper 类型的值)。
    • Foo<in T> 等价于 Foo<in Nothing>(无法安全地写入)。
    • Foo<T : TUpper> 等价于读取时 Foo<out TUpper>,写入时 Foo<in Nothing>。
    • 类似于 Java 的原始类型,但更安全。

    泛型函数

    • 声明:将类型参数放在函数名之前。
    CodeBlock Loading...
    • 调用:显式指定类型参数,或者让编译器推断。
    CodeBlock Loading...

    泛型约束 (Generic Constraints)

    • 概念:限制可以替代类型参数的类型范围。
    • 上界 (Upper Bounds):使用冒号 : 指定上界,类似于 Java 的 extends。
    CodeBlock Loading...
    • where 子句:如果需要多个上界,则使用 where 子句。
    CodeBlock Loading...

    明确的非空类型 (Definitely Non-nullable Types)

    • 使用 T & Any 声明类型参数为明确的非空类型。
    • 通常用于与带有 @NotNull 注解的 Java 方法互操作。
    CodeBlock Loading...

    类型擦除 (Type Erasure)

    • Kotlin 在编译时执行泛型类型安全检查。
    • 在运行时,泛型类型的实例不包含关于其实际类型参数的任何信息。
    • Foo<Bar> 和 Foo<Baz?> 的实例在运行时都被擦除为 Foo<*>。

    泛型类型检查和转换

    • 由于类型擦除,无法在运行时检查泛型类型实例是否使用特定类型参数创建(例如,无法使用 ints is List<Int> 检查)。
    • 可以针对星号投影类型检查实例(例如,something is List<*>)。
    • 在已静态检查类型参数的情况下,可以执行不涉及类型参数的非泛型部分的类型检查或转换(例如,list as ArrayList)。
    • 泛型函数调用的类型参数仅在编译时检查。在函数体内部,类型参数不能用于类型检查,并且到类型参数的类型转换是未经检查的。唯一的例外是具有具体化类型参数的内联函数,它们具有在每个调用站点内联的实际类型参数。

    未经检查的转换 (Unchecked Casts)

    • 无法在运行时检查到具有具体类型参数的泛型类型的类型转换(例如,foo as List<String>)。
    • 这些未经检查的转换可以在类型安全由高级程序逻辑暗示但编译器无法直接推断时使用。
    • 可以通过用 @Suppress("UNCHECKED_CAST") 注释语句或声明来禁止显示未经检查的转换警告。

    下划线运算符 (Underscore Operator) 用于类型参数

    • 可以使用下划线运算符 _ 用于类型参数. 当其他类型被显式指定时,使用它来自动推断参数的类型.

    数据类 (Data Classes)

    核心概念

    • Kotlin 中的数据类主要用于保存数据。对于每个数据类,编译器会自动生成额外的成员函数,允许将实例打印为可读的输出、比较实例、复制实例等。数据类用 data 标记。
    CodeBlock Loading...

    自动生成的成员

    • 对于在主构造函数中声明的所有属性,编译器会自动派生以下成员:
      • equals()/hashCode() 对。
      • toString() 格式为 "User(name=John, age=42)"。
      • 与属性声明顺序相对应的 componentN() 函数。
      • copy() 函数。

    数据类的要求

    • 为确保生成代码的一致性和有意义的行为,数据类必须满足以下要求:
      • 主构造函数必须至少有一个参数。
      • 所有主构造函数参数必须标记为 val 或 var。
      • 数据类不能是 abstract、open、sealed 或 inner。

    成员继承规则

    • 如果在数据类主体中显式实现了 equals()、hashCode() 或 toString(),或者在超类中显式实现了最终实现,则不会生成这些函数,并且使用现有实现。
    • 如果超类型具有 componentN() 函数(它们是 open 并返回兼容类型),则会为数据类生成相应的函数并覆盖超类型的函数。如果由于签名不兼容或由于它们是 final 而无法覆盖超类型的函数,则会报告错误。
    • 不允许为 componentN() 和 copy() 函数提供显式实现。
    • 数据类可以扩展其他类。

    JVM 参数为空的构造函数

    • 在 JVM 上,如果生成的类需要具有无参数构造函数,则必须为属性指定默认值:
    CodeBlock Loading...

    在类主体中声明的属性

    • 编译器仅使用在主构造函数内部定义的属性来自动生成函数。要从生成的实现中排除属性,请在类主体中声明它:
    CodeBlock Loading...
    • 在该示例中,默认情况下只有 name 属性在 toString()、equals()、hashCode() 和 copy() 实现中使用,并且只有一个组件函数 component1()。age 属性是在类主体中声明的,因此被排除在外。因此,具有相同 name 但不同 age 值的两个 Person 对象被认为是相等的,因为 equals() 仅评估来自主构造函数的属性:
    CodeBlock Loading...

    复制 (Copying)

    • 使用 copy() 函数复制对象,允许你在保持其余属性不变的情况下更改某些属性。上面 User 类的这个函数的实现如下:
    CodeBlock Loading...
    CodeBlock Loading...

    数据类和解构声明 (Destructuring Declarations)

    • 为数据类生成的组件函数使它们可以在解构声明中使用:
    CodeBlock Loading...

    标准数据类

    • 标准库提供了 Pair 和 Triple 类。但是,在大多数情况下,命名的数据类是更好的设计选择,因为它们通过为属性提供有意义的名称来使代码更易于阅读。

    枚举类 (Enum Classes)

    核心概念

    • Kotlin 的枚举类用于表示一组有限的不同值,每个值都是枚举类的一个实例。

    基本用法

    • 声明:使用 enum class 关键字声明枚举类。
    CodeBlock Loading...
    • 常量:枚举常量是类的对象,以逗号分隔。

    初始化

    • 构造函数:枚举常量可以像类一样进行初始化,使用构造函数传递参数。
    CodeBlock Loading...

    匿名类

    • 定义:枚举常量可以声明自己的匿名类,包含各自的方法,以及覆写基类的方法。
    CodeBlock Loading...
    • 成员定义:如果枚举类定义了任何成员,则使用分号将常量定义与成员定义分隔开。

    实现接口

    • 实现接口:枚举类可以实现接口(但不能继承类),可以为所有条目提供接口成员的公共实现,也可以在每个条目的匿名类中提供单独的实现。
    CodeBlock Loading...
    • 默认情况下,所有枚举类都实现 Comparable 接口。枚举类中的常量以自然顺序定义,可以进行比较。

    枚举常量的使用

    • Kotlin 中的枚举类具有用于列出定义的枚举常量和按名称获取枚举常量的合成属性和方法。

      • EnumClass.valueOf(value: String): EnumClass:根据名称返回枚举常量。如果指定的名称与类中定义的任何枚举常量都不匹配,则 valueOf() 方法会抛出 IllegalArgumentException。
      • EnumClass.entries: EnumEntries<EnumClass>:Kotlin 1.9 引入,返回一个专门的 List<EnumClass> 类型的枚举条目。Kotlin 2.0 将替代 enumValues<T>(),它返回一个数组.

        CodeBlock Loading...
    • 每个枚举常量还有属性:

      • name:获取枚举常量的名称。
      • ordinal:获取枚举常量在枚举类声明中的位置(从 0 开始)。

        CodeBlock Loading...

    泛型方式访问枚举常量

    • enumEntries<T>():Kotlin 2.0 引入,返回给定枚举类型 T 的所有枚举条目的列表,推荐使用,性能好。
    CodeBlock Loading...

    内联值类

    核心概念

    • 内联值类是一种特殊的类,它包装了一个单一的值,但在运行时不会创建实际的类实例,从而减少了内存占用和提高了性能。

    声明和使用

    • 声明:使用 value class 关键字声明内联值类。在 JVM 上,需要加上 @JvmInline 注解。
    CodeBlock Loading...
    • 特点:
      • 只能有一个属性,且必须在构造函数中初始化。
      • 性能优化:在运行时,内联值类的实例直接使用底层值,不会创建新的对象。
      • 类型安全:虽然底层是 String,但 Password 类型不能直接当作 String 使用,反之亦然。这与 typealias 不同,typealias 只是给类型起个别名,可以互相替换。

    使用场景

    • 当你需要创建一个新的类型,但又不想牺牲性能时。
    • 当你需要对现有的类型进行更强的约束时。

    示例

    CodeBlock Loading...

    object 关键字

    单例对象 (Singleton Objects)

    • 定义:object 关键字用于声明一个单例类,即只存在一个实例的类。
    • 特点:
      • 单例对象在第一次被访问时创建,并且在整个应用程序生命周期内都存在。
      • 单例对象不能有构造函数。
      • 可以继承类或实现接口。
    CodeBlock Loading...

    伴生对象 (Companion Objects)

    • 定义:companion object 关键字用于在类中声明一个伴生对象。
    • 特点:
      • 伴生对象与类关联,但不是类的实例。
      • 伴生对象可以访问类的私有成员。
      • 伴生对象通常用于创建类的工厂方法或存储类的静态成员。
    CodeBlock Loading...

    匿名对象 (Anonymous Objects)

    • 定义:object : ClassName() 用于创建匿名对象,即没有名称的对象。
    • 特点:
      • 匿名对象通常用于实现接口或继承类,并提供一次性的实现。
      • 可以访问外部作用域的变量。
      • 立即执行和初始化。
    CodeBlock Loading...

    对象声明 vs. 对象表达式

    特性 Object 声明 (Singleton) Object 表达式 (匿名对象)
    用途 创建单例 一次性使用
    初始化时机 首次访问时懒加载 立即初始化
    访问方式 通过名称直接访问 表达式结果 (变量引用)
    命名 必须有名称 无需命名

    包 (Packages) 和导入 (Imports)

    核心概念

    • 包 (Package):用于组织 Kotlin 代码,类似于文件夹。每个源文件可以声明属于哪个包。
    • 导入 (Import):允许在代码中使用其他包中的类、函数等,而无需写完整的包名。

    关键信息

    1. 包声明 (Package Declaration)

      • 在源文件的开头使用 package 关键字声明包名:package org.example
      • 源文件中的所有内容(类、函数等)都属于该包。
      • 例如:org.example.printMessage 和 org.example.Message
      • 如果没有指定包,则属于默认包(没有名称)。
    2. 默认导入 (Default Imports)

      • Kotlin 默认导入一些常用的包,无需手动导入。
      • 包括 kotlin.*、kotlin.annotation.*、kotlin.collections.*、等等...
      • 根据目标平台 (JVM, JS),还会额外导入一些包,例如 java.lang.* (JVM) 或 kotlin.js.* (JS)。
    3. 自定义导入 (Custom Imports)

      • 可以使用 import 关键字导入单个名称或整个包。
      • 导入单个名称:import org.example.Message (可以直接使用 Message)
      • 导入整个包:import org.example.* (可以使用 org.example 中的所有内容)
    4. 别名 (Alias)

      • 如果导入的名称发生冲突,可以使用 as 关键字重命名导入的实体,解决冲突。
      • 示例:

        CodeBlock Loading...
    5. 可以导入的内容

      • 顶层函数和属性
      • 对象声明中的函数和属性
      • 枚举常量
    6. 顶层声明的可见性

      • 如果顶层声明(例如顶层函数、顶层变量)被标记为 private,则它只能在该声明的文件中访问。

    接口 (Interfaces)

    核心概念

    • Kotlin 的接口与 Java 8+ 的接口类似,可以包含抽象方法的声明和方法的实现。关键区别在于接口不能存储状态。它们可以有属性,但这些属性必须是抽象的或提供访问器实现。

    定义接口

    • 使用 interface 关键字定义接口:
    CodeBlock Loading...

    实现接口

    • 一个类或对象可以实现一个或多个接口:
    CodeBlock Loading...
    • 使用冒号 (:) 表示继承或实现关系。
    • 必须使用 override 关键字重写接口中的方法。

    接口中的属性

    • 接口可以声明属性。
    • 接口中的属性不能有幕后字段 (backing fields)。
    • 接口中的属性可以是抽象的,或者提供访问器 (accessor) 实现:
    CodeBlock Loading...

    接口继承

    • 接口可以继承其他接口。
    • 继承的接口可以提供成员的实现,也可以声明新的函数和属性。
    • 实现类只需要定义缺失的实现:
    CodeBlock Loading...

    解决覆盖冲突

    • 当一个类实现多个接口时,可能会继承同一个方法的多个实现。此时,必须显式地指定如何实现该方法:
    CodeBlock Loading...
    • 如果继承了多个具有相同签名的方法,并且你想要调用特定接口的实现,可以使用 super<接口名>.方法名() 来指定。
    • 即使只继承了一个实现(例如 bar()),仍然需要重写并指定实现,以明确类的行为。这是因为编译器无法自动确定应该使用哪个接口的默认实现。如果不重写,会编译错误。

    函数式 (SAM) 接口

    核心概念

    • 函数式接口(Functional Interface),也称为单一抽象方法 (Single Abstract Method, SAM) 接口,是指只有一个抽象成员函数的接口。它可以有多个非抽象成员函数,但只能有一个抽象成员函数。Kotlin 提供了 fun interface 关键字来声明函数式接口,并支持 SAM 转换,允许使用 lambda 表达式代替手动实现类,使代码更加简洁。

    声明函数式接口

    • 使用 fun interface 关键字声明:
    CodeBlock Loading...

    SAM 转换 (SAM Conversions)

    • 概念:SAM 转换允许你使用 lambda 表达式代替手动创建实现函数式接口的类。Kotlin 会自动将与接口的单一抽象方法签名匹配的 lambda 表达式转换为接口的实例。
    • 优点:简化代码,提高可读性。
    • 示例:
    CodeBlock Loading...
    • 在这个例子中,lambda 表达式 { it % 2 == 0 } 被自动转换为了 IntPredicate 接口的实现。

    • Java 接口也支持 SAM 转换

    从带构造函数的接口迁移到函数式接口

    • 在 Kotlin 1.6.20 及更高版本中,支持对函数式接口构造函数的调用引用 (callable references)。这为从带有构造函数函数的接口迁移到函数式接口提供了一种源兼容的方式。

    • 示例:

    CodeBlock Loading...
    • 保持二进制兼容性:使用 @Deprecated 注解,将传统函数 Printer 标记为隐藏 (DeprecationLevel.HIDDEN)。
    CodeBlock Loading...

    函数式接口 vs. 类型别名 (Type Aliases)

    • 虽然类型别名也可以简化函数类型的使用,但函数式接口和类型别名有着不同的目的:

      • 类型别名:只是现有类型的别名,不会创建新类型。

        CodeBlock Loading...
      • 函数式接口:创建一个新的类型。

        CodeBlock Loading...

    区别总结

    特性 类型别名 函数式接口
    类型创建 不创建新类型 创建新类型
    成员 只能有一个成员(函数类型) 可以有多个非抽象成员函数和一个抽象成员函数
    接口扩展 不支持实现/继承其他接口 支持实现/继承其他接口
    扩展函数 扩展函数会应用于所有相同签名的函数类型,不限于特定的别名。 扩展函数只适用于该函数式接口的实例,不会影响其他函数类型。
    使用场景 用于简化函数类型,当 API 需要接受具有特定参数和返回类型的函数(任何函数)时。 用于定义更复杂的实体,例如具有非平凡的约定和/或操作,这些约定和/或操作无法用函数类型的签名表示。
    性能 开销更小 可能有一定开销,例如转成 specific interface

    如何选择

    • 类型别名:当 API 需要接受一个具有特定参数和返回类型的函数 (任何函数) 时,使用类型别名或简单的函数类型。
    • 函数式接口:当 API 需要接受一个比函数更复杂的实体,例如,具有非平凡的约定和/或操作,并且无法用函数类型的签名来表示时,使用函数式接口。

    可见性修饰符

    核心概念

    • 可见性修饰符控制类、对象、接口、构造函数、函数、属性及其 setter 的可见范围。Getter 的可见性始终与其属性相同。Kotlin 提供了四种可见性修饰符:private、protected、internal 和 public。默认可见性是 public。

    可见性修饰符概览

    修饰符 描述
    public (默认) 声明在任何地方都可见。
    private 声明只在声明它的文件或类中可见。
    protected 声明只在声明它的类及其子类中可见。
    internal 声明只在同一模块内可见。 模块是一组一起编译的 Kotlin 文件。

    包级别声明 (Package-level Declarations)

    • 在包内直接声明的函数、属性、类、对象和接口称为顶层声明。

      • public (默认):任何地方都可见。
      • private:只在包含该声明的文件内可见。
      • internal:在同一模块内的任何地方都可见。
      • protected:不适用于顶层声明。
    CodeBlock Loading...
    • 要使用来自另一个包的可见顶层声明,需要导入它。

    类成员 (Class Members)

    • 对于在类内部声明的成员:

      • private:只在该类内部可见(包括所有成员)。
      • protected:与 private 具有相同的可见性,但在子类中也可见。
      • internal:同一模块内,看到该声明类的客户端都能看到其 internal 成员。
      • public:看到该声明类的客户端都能看到其 public 成员。
    CodeBlock Loading...
    • 外部类不能看到其内部类的 private 成员。
    • 如果重写 protected 或 internal 成员,并且没有显式指定可见性,则重写成员将具有与原始成员相同的可见性。

    构造函数 (Constructors)

    • 可以使用以下语法来指定类的主构造函数的可见性:
    CodeBlock Loading...
    • 默认情况下,所有构造函数都是 public,这实际上等同于它们在类可见的任何地方都可见(这意味着 internal 类的构造函数仅在同一模块内可见)。
    • 对于密封类 (sealed classes),构造函数默认为 protected。

    局部声明 (Local Declarations)

    • 局部变量、函数和类不能具有可见性修饰符。

    模块 (Modules)

    • internal 可见性修饰符表示成员在同一模块内可见。具体来说,模块是指一起编译的一组 Kotlin 文件,例如:
      • IntelliJ IDEA 模块。
      • Maven 项目。
      • Gradle 源码集(例外情况是测试源码集可以访问 main 的 internal 声明)。
      • 使用单个 <kotlinc> Ant 任务调用编译的一组文件。

    委托属性 (Delegated Properties)

    核心概念

    • 委托属性是一种强大的 Kotlin 特性,它允许将属性的 get() 和 set() 方法委托给另一个对象(委托对象),从而实现代码复用、通用行为,并简化属性的实现逻辑。委托属性非常适合实现如延迟初始化、属性变更监听、数据存储等常见的编程模式。

    基本语法

    kotlin

    复制

    CodeBlock Loading...
    • <属性名>:你要声明的属性的名称。
    • <类型>:属性的类型,必须与委托对象 getValue() 方法的返回值类型相匹配。
    • <表达式>:一个返回委托对象的表达式。这个对象负责实际的属性访问和修改逻辑。
    • by:关键字,指示 Kotlin 使用委托机制。

    委托对象的要求

    • 委托对象必须提供特定签名的 getValue() 函数(对于 val 和 var 属性)和 setValue() 函数(仅对于 var 属性)。这些函数需要声明为 operator。

      • getValue() 函数 (必须):

        kotlin

        复制

        CodeBlock Loading...

        • thisRef:(Any?) 对包含属性的对象的引用。如果该属性是顶层属性,则此值为 null。此参数允许委托访问其所属对象的状态(尽管通常不建议)。
        • property:(KProperty<*>) 一个 KProperty 实例,表示被委托的属性。它可以用于获取属性的元数据,如名称 (property.name)。
        • 返回值类型:委托属性的类型。getValue() 必须返回该类型或其子类型的值。
      • setValue() 函数 (仅用于 var 属性):

        CodeBlock Loading...
        • thisRef:(Any?) 对包含属性的对象的引用(如同 getValue())。
        • property:(KProperty<*>) 表示被委托的属性的 KProperty 实例(如同 getValue())。
        • value:(类型与属性相同) 要分配给属性的新值。setValue() 应该执行将此值存储到支持存储中的操作。
        • 新值类型:委托属性的类型。setValue() 必须接受该类型或其父类型的参数。

    标准委托 (Standard Delegates)

    Kotlin 标准库提供了一组有用的预定义委托,可以通过 kotlin.properties.Delegates 和 kotlin 包访问:

    • lazy():延迟初始化属性。属性的值仅在第一次访问时通过提供的 lambda 表达式计算。后续访问将返回缓存的值。

      CodeBlock Loading...
      • LazyThreadSafetyMode:控制延迟初始化如何处理多线程访问。
        • SYNCHRONIZED (默认):保证只有一个线程初始化该值。这是线程安全的,但可能导致性能开销。
        • PUBLICATION:允许多个线程尝试初始化该值,但保证只有一个线程的计算结果将被使用。如果初始化 lambda 表达式是幂等的(多次执行产生相同的结果),则可以使用此选项。
        • NONE:不提供任何线程安全保证。如果属性只会在单个线程中访问,则可以使用此选项以获得更高的性能。
    • Delegates.observable():创建一个可观察的属性。每次属性值被分配新值时,都会调用提供的处理程序。

      CodeBlock Loading...
      • 处理程序函数的签名是 (KProperty<*>, oldValue, newValue) -> Unit。
    • Delegates.vetoable():创建一个可否决的属性。提供的处理程序会在每次属性尝试被分配新值之前被调用。如果处理程序返回 true,则分配会发生;如果返回 false,则分配会被阻止。

      CodeBlock Loading...
      • 处理程序函数的签名是 (KProperty<*>, oldValue, newValue) -> Boolean。

    委托给另一个属性

    Kotlin 允许一个属性将其 getter 和 setter 委托给同一类或不同类中的另一个属性。这对于实现向后兼容性重命名或同步多个属性非常有用。

    CodeBlock Loading...
    • 在这个例子中, delegatedToMember 的 getter 和 setter 都委托给了 memberInt。修改 delegatedToMember 也会影响 memberInt, 反之亦然.
    • @Deprecated: 提示用户应该使用 newName 代替 oldName

    将属性存储在 Map 中

    可以使用 Map 或 MutableMap 作为委托对象,允许你使用字符串键从 Map 中动态检索属性值。这在处理 JSON 或其他动态数据结构时很有用。

    CodeBlock Loading...
    • 在这个例子中, User 类的构造函数接收一个 Map<String, Any?>. name 和 age 属性的值是从这个 map 中使用字符串键 "name" 和 "age" 检索的。

    局部委托属性

    局部委托属性允许你在函数或代码块内部声明委托属性。它们对于缓存计算或实现仅在特定条件下才需要的局部变量的延迟初始化非常有用。

    CodeBlock Loading...
    • 在这个例子中, processedData 仅当 data 不是 null 并且不是空字符串时才被初始化。如果 data 为 null 或为空,则不会执行初始化 lambda 表达式。

    provideDelegate 函数

    provideDelegate 函数允许你拦截委托属性的创建过程。这对于验证属性的正确使用、设置自定义委托实例或执行其他初始化逻辑很有用。

    CodeBlock Loading...
    • thisRef:对包含属性的对象的引用(如同 getValue())。
    • property:表示被委托的属性的 KProperty 实例(如同 getValue())。
    • 返回值:作为属性委托的实际对象。它可以与原始委托对象不同。允许用自定义的 Delegate 替换用户提供的 Delegate。
    CodeBlock Loading...
    • provideDelegate 函数在属性创建时调用,允许在实际委托对象被使用之前执行逻辑。示例打印一条消息指示属性及其ID被绑定,然后才创建实际的ResourceDelegate,它负责延迟加载资源。

    委托属性的翻译规则

    当 Kotlin 编译器遇到委托属性时,它会生成以下辅助代码:

    1. 隐藏的 $delegate 属性:对于 var prop: Type by MyDelegate(),编译器会创建一个隐藏的属性,例如 prop$delegate,类型为 MyDelegate。这个属性存储了实际的委托对象。
    2. getter 和 setter 方法:编译器会生成 prop 的 getter 和 setter 方法。这些方法会调用委托对象的 getValue() 和 setValue() 方法。

      CodeBlock Loading...
    3. 优化场景:在某些情况下,如直接委托给另一个属性,编译器为了优化性能,会直接访问委托属性的幕后字段,而省略 $delegate 属性的生成。

    扩展 (Extensions)

    核心概念

    • Kotlin 扩展允许在不继承类或使用装饰器等设计模式的情况下,向类或接口添加新的功能。通过特殊声明(称为扩展)来实现。可以为第三方库中的类或接口编写新的函数,而无需修改它们。

    扩展函数 (Extension Functions)

    • 声明:在函数名前面加上接收者类型。
    CodeBlock Loading...
    • this 关键字在扩展函数中指代接收者对象(在点号之前传递的对象)。

    • 调用:像调用普通方法一样调用扩展函数。

    CodeBlock Loading...

    扩展的静态解析

    • 扩展实际上并不修改它们所扩展的类。定义扩展并不会将新成员插入到类中,而只是使新的函数能够通过点号标记法在变量上调用。
    • 扩展函数是静态分发的。在编译时根据接收者类型确定调用哪个扩展函数。
    CodeBlock Loading...
    • 成员优先:如果一个类有一个成员函数,并且定义了一个具有相同接收者类型、相同名称并且适用于给定参数的扩展函数,则成员函数总是优先。
    CodeBlock Loading...
    • 可以重载具有相同名称但不同签名的成员函数。
    CodeBlock Loading...

    可空接收者 (Nullable Receiver)

    • 可以定义具有可空接收者类型的扩展。这些扩展可以在对象变量的值为 null 时调用。如果接收者为 null,则 this 也为 null。建议在函数体内部执行 this == null 检查,以避免编译器错误。
    CodeBlock Loading...

    扩展属性 (Extension Properties)

    • Kotlin 支持类似于函数的扩展属性。
    CodeBlock Loading...
    • 由于扩展实际上并没有将成员插入到类中,因此扩展属性没有幕后字段 (backing field) 的有效方法。这就是为什么不允许使用初始化器进行扩展属性的原因。扩展属性的行为只能通过显式提供 getter/setter 来定义。
    CodeBlock Loading...

    伴生对象扩展 (Companion Object Extensions)

    • 如果一个类定义了一个伴生对象,你也可以为该伴生对象定义扩展函数和属性。就像伴生对象的常规成员一样,它们可以通过仅使用类名作为限定符来调用。
    CodeBlock Loading...

    扩展的作用域 (Scope of Extensions)

    • 大多数情况下,你在顶层(直接在包下)定义扩展:
    CodeBlock Loading...
    • 要在其声明包外部使用扩展,请在使用点导入它:
    CodeBlock Loading...

    将扩展声明为成员 (Declaring Extensions as Members)

    • 可以在一个类内部声明另一个类的扩展。在这样的扩展中,存在多个隐式接收者 - 可以访问其成员而无需限定符的对象。在其中声明扩展的类的实例称为分发接收者 (dispatch receiver),扩展方法的接收者类型的实例称为扩展接收者 (extension receiver)。
    CodeBlock Loading...
    • 如果分发接收者的成员和扩展接收者的成员之间存在名称冲突,则扩展接收者优先。要引用分发接收者的成员,可以使用限定的 this 语法。
    CodeBlock Loading...
    • 声明为成员的扩展可以声明为 open 并在子类中重写。这意味着此类函数的调度对于分发接收者类型是虚拟的,但对于扩展接收者类型是静态的。
    CodeBlock Loading...

    可见性注意事项

    • 扩展使用与在同一作用域中声明的常规函数相同的可见性修饰符。例如:
      • 在文件顶层声明的扩展可以访问同一文件中的其他 private 顶层声明。
      • 如果扩展在其接收者类型之外声明,则它无法访问接收者的 private 或 protected 成员。

    类型别名 (Type Aliases)

    核心概念

    • 类型别名就像给一个类型起了个“外号”。你用“外号”和用真名效果完全一样,但“外号”可能更短、更好记。

    用途

    • 简化长类型名称:比如 Set<Network.Node> 这么长的类型,可以起个简单点的名字,比如 NodeSet。这在处理复杂的泛型类型时特别有用。

    • 给函数类型起别名:比如 (Int, String, Any) -> Unit 这个函数类型,可以起个别名 MyHandler,方便阅读和使用。

    • 给内部类/嵌套类起别名:如果多个类都有内部类,起个别名能让你更容易区分,例如 A.Inner 和 B.Inner 可以分别用 AInner 和 BInner 来代替。

    示例

    CodeBlock Loading...

    重要:类型别名不是新类型!

    • typealias Predicate<T> = (T) -> Boolean 只是给 (T) -> Boolean 起了个别名。
    • 编译器在编译时,会将所有类型别名替换成它对应的原始类型。
    • 这意味着,你可以随意地用别名类型代替原始类型,反之亦然,kotlin编译器都认为完全一样。

    示例说明

    CodeBlock Loading...

    函数 (Functions)

    核心概念

    • 函数是代码的基本构建块,用于执行特定任务。Kotlin 提供了灵活的方式来定义和使用函数。

    函数声明

    • Kotlin 函数使用 fun 关键字声明。
    • 声明格式:fun 函数名(参数名: 参数类型): 返回类型 { 函数体 }
    CodeBlock Loading...

    函数使用 (调用)

    • 使用标准方式调用函数:函数名(参数)
    • 调用成员函数(类或对象中的函数)使用点表示法:实例.函数名(参数)
    CodeBlock Loading...

    参数

    • 函数参数使用 Pascal 符号定义:参数名: 参数类型
    • 多个参数用逗号分隔。
    • 每个参数必须显式指定类型。
    CodeBlock Loading...
    • 可以使用尾随逗号来声明函数参数:
    CodeBlock Loading...

    默认参数

    • 函数参数可以有默认值,当调用函数时省略相应的参数,则使用默认值。这减少了函数重载的数量。
    • 默认值通过在类型后添加 = 来设置,例如 参数名: 参数类型 = 默认值
    CodeBlock Loading...
    • 重写的方法总是使用基类方法的默认参数值。当重写一个具有默认参数值的方法时,默认参数值必须从签名中省略:
    CodeBlock Loading...
    • 如果一个默认参数在一个没有默认值的参数之前,则默认值只能通过使用命名参数来调用函数来使用:
    CodeBlock Loading...
    • 如果默认参数之后的最后一个参数是一个 lambda 表达式,则可以将它作为命名参数或在括号外传递:
    CodeBlock Loading...

    命名参数

    • 可以在调用函数时命名一个或多个函数的参数。当函数有很多参数,并且很难将值与参数关联时,这很有帮助,尤其是当参数是布尔值或 null 值时。
    • 当在函数调用中使用命名参数时,可以自由地更改它们在列表中列出的顺序。如果想使用它们的默认值,可以完全省略这些参数。
    • 考虑 reformat() 函数,它有 4 个具有默认值的参数。
    CodeBlock Loading...
    • 在调用此函数时,不必命名其所有参数:
    CodeBlock Loading...
    • 可以跳过所有具有默认值的参数:
    CodeBlock Loading...
    • 也可以跳过具有默认值的特定参数,而不是省略所有参数。但是,在第一个跳过的参数之后,必须命名所有后续参数:
    CodeBlock Loading...
    • 可以使用 spread 操作符 (用 * 作为数组的前缀) 通过名称传递可变数量的参数 (vararg):
    CodeBlock Loading...
    • 在 JVM 上调用 Java 函数时,不能使用命名参数语法,因为 Java 字节码并不总是保留函数参数的名称。

    Unit 返回函数

    • 如果函数不返回有用的值,则其返回类型为 Unit。Unit 是一种只有一个值的类型——Unit。这个值不必显式返回:
    CodeBlock Loading...
    • Unit 返回类型声明也是可选的。上面的代码等价于:
    CodeBlock Loading...

    单表达式函数

    • 当函数体由单个表达式组成时,可以省略花括号,并在 = 符号后指定函数体:
    CodeBlock Loading...
    • 当编译器可以推断出返回类型时,显式声明返回类型是可选的:
    CodeBlock Loading...

    显式返回类型

    • 具有块状函数体的函数必须始终显式指定返回类型,除非打算让它们返回 Unit,在这种情况下,指定返回类型是可选的。
    • Kotlin 不会推断具有块状函数体的函数的返回类型,因为此类函数的主体可能具有复杂的控制流,并且返回类型对于读者(有时甚至对于编译器)来说并不明显。

    可变数量的参数 (Varargs)

    • 可以使用 vararg 修饰符标记函数的参数(通常是最后一个参数):
    CodeBlock Loading...
    • 在这种情况下,可以向函数传递可变数量的参数:
    CodeBlock Loading...
    • 在函数内部,类型 T 的 vararg 参数显示为 T 的数组,如上面的示例所示,其中 ts 变量的类型为 Array<out T>。
    • 只能将一个参数标记为 vararg。如果 vararg 参数不是列表中的最后一个参数,则必须使用命名参数语法传递后续参数的值,或者,如果参数具有函数类型,则通过在括号外传递 lambda 表达式来传递后续参数的值。
    • 当调用一个 vararg 函数时,可以单独传递参数,例如 asList(1, 2, 3)。如果已经有一个数组并且想将它的内容传递给函数,请使用 spread 操作符(用 * 作为数组的前缀):
    CodeBlock Loading...
    • 如果想将原始类型数组传递到 vararg 中,则需要使用 toTypedArray() 函数将其转换为常规(类型化)数组:
    CodeBlock Loading...

    中缀符号 (Infix Notation)

    • 用 infix 关键字标记的函数也可以使用中缀符号调用(省略调用中的点和括号)。中缀函数必须满足以下要求:
      • 它们必须是成员函数或扩展函数。
      • 它们必须只有一个参数。
      • 该参数不能接受可变数量的参数并且必须没有默认值。
    CodeBlock Loading...
    • 中缀函数调用的优先级低于算术运算符、类型转换和 rangeTo 运算符。以下表达式是等效的:

      • 1 shl 2 + 3 等价于 1 shl (2 + 3)
      • 0 until n * 2 等价于 0 until (n * 2)
      • xs union ys as Set<*> 等价于 xs union (ys as Set<*>)
    • 另一方面,中缀函数调用的优先级高于布尔运算符 && 和 ||、is 和 in 检查以及一些其他运算符。这些表达式也是等效的:

      • a && b xor c 等价于 a && (b xor c)
      • a xor b in c 等价于 (a xor b) in c
    • 注意,中缀函数总是需要指定接收者和参数。当使用中缀符号在当前接收者上调用方法时,请显式使用 this。这是为了确保明确的解析。

    CodeBlock Loading...

    函数范围 (Function Scope)

    • Kotlin 函数可以在文件的顶层声明,这意味着不需要创建一个类来保存一个函数,这是 Java、C# 和 Scala 等语言中必需的(自 Scala 3 起,顶层定义可用)。除了顶层函数之外,Kotlin 函数还可以局部声明为成员函数和扩展函数。

    局部函数

    • Kotlin 支持局部函数,局部函数是其他函数内部的函数:
    CodeBlock Loading...
    • 局部函数可以访问外部函数的局部变量(闭包)。在上面的例子中,visited 可以是一个局部变量:
    CodeBlock Loading...

    成员函数

    • 成员函数是在类或对象内部定义的函数:
    CodeBlock Loading...
    • 成员函数使用点表示法调用:
    CodeBlock Loading...

    泛型函数

    • 函数可以具有泛型参数,这些参数使用尖括号在函数名称之前指定:
    CodeBlock Loading...

    尾递归函数

    • Kotlin 支持一种称为尾递归的函数式编程风格。对于某些通常使用循环的算法,可以使用递归函数来代替,而没有堆栈溢出的风险。当一个函数用 tailrec 修饰符标记并且满足所需的正式条件时,编译器会优化掉递归,留下一个快速而高效的基于循环的版本:
    CodeBlock Loading...
    • 要符合 tailrec 修饰符的条件,函数必须将自身调用作为它执行的最后一个操作。当递归调用之后有更多代码、在 try/catch/finally 块中或在 open 函数上时,不能使用尾递归。目前,Kotlin for the JVM 和 Kotlin/Native 支持尾递归。

    高阶函数和 Lambda 表达式

    核心概念

    • 函数也是值:在 Kotlin 里,函数可以像数字、字符串一样,赋值给变量、存到数据结构里、当做参数传递给其他函数,或者从函数里返回。
    • 高阶函数:简单说,就是能接收函数作为参数或者返回一个函数的函数。
    • Lambda表达式:简单说,就是匿名函数,可以快速定义一个函数并使用。

    什么是高阶函数?

    • 定义:接收函数作为参数或返回一个函数的函数。
    • 例子:fold 函数就是一个典型的高阶函数。fold函数接收一个初始值和一个组合函数。它会遍历集合中的每一个元素,使用组合函数将当前元素和累加值组合起来,最终返回一个结果。

      • 代码示例 (简化):

        CodeBlock Loading...
        • combine 参数就是一个函数类型,它接收两个参数(R 和 T),返回一个 R 类型的值。
    • 如何调用 fold:使用 Lambda 表达式或者函数引用。

      • 例子:

        CodeBlock Loading...

    函数类型

    • 作用:Kotlin 使用函数类型来表示函数,例如 (Int) -> String 表示一个接收 Int 参数并返回 String 的函数。
    • 语法:(参数类型1, 参数类型2, ...) -> 返回值类型。
      • () 表示没有参数,如 () -> Unit。Unit 相当于java中的void。
      • A.(B) -> C 表示一个带有接收者(receiver)的函数类型。它可以看作A类型的扩展函数。
    • 可空函数类型:((Int, Int) -> Int)? 表示这个函数类型可以为 null。
    • 类型别名:可以使用 typealias 给函数类型起一个别名,方便使用。
    CodeBlock Loading...

    如何创建一个函数类型的实例?

    有以下几种方法:

    • Lambda 表达式:{ a, b -> a + b }
    • 匿名函数:fun(s: String): Int { return s.toIntOrNull() ?: 0 }
    • 函数引用:::isOdd (顶层函数), String::toInt (成员函数)
    • 实现函数类型接口的类:
    CodeBlock Loading...

    调用函数类型实例

    • 使用 invoke(...):f.invoke(x)
    • 直接调用:f(x)
    • 如果函数类型有接收者,可以像调用扩展函数一样:1.foo(2)。

    闭包

    • 定义:Lambda 表达式或匿名函数可以访问外部作用域中定义的变量。
    • 修改外部变量:Lambda 表达式可以修改外部作用域中的变量。
    CodeBlock Loading...

    带有接收者的函数字面值

    • 类型:A.(B) -> C
    • 作用:可以在 Lambda 表达式中像调用扩展函数一样调用接收者对象的方法。
    • this 关键字:在 Lambda 表达式中,可以使用 this 关键字引用接收者对象。
    • 例子:
    CodeBlock Loading...

    类型安全构建器

    • 带有接收者的 Lambda 表达式常用于构建类型安全的 DSL (领域特定语言)。

    Lambda 表达式

    • 语法:{ 参数 -> 函数体 }
    • 参数类型推断:如果编译器可以推断出参数类型,可以省略参数类型。
    • 尾随 Lambda:如果函数的最后一个参数是函数类型,可以将 Lambda 表达式放在括号外面。
      • numbers.filter { it > 0 }
    • it 关键字:如果 Lambda 表达式只有一个参数,可以使用 it 关键字来引用该参数。
      • numbers.map { it * 2 }
    • 返回值:Lambda 表达式中最后一个表达式的值就是返回值。也可以使用 return@label 显式返回。
    • 下划线 _:如果 Lambda 表达式的参数没有使用,可以使用下划线 _ 代替参数名。
      • map.forEach { (_, value) -> println(value) }

    匿名函数

    • 语法:fun(参数: 类型): 返回值类型 { 函数体 }
    • 特点:
      • 可以显式指定返回值类型。
      • return 语句从匿名函数本身返回,而不是从包含它的函数返回 (与 Lambda 表达式不同)。

    内联函数

    核心概念

    • 内联函数是一种特殊的函数,它允许编译器在调用点直接插入函数体,而不是像普通函数那样进行函数调用。这可以减少函数调用的开销,提高性能,特别是在函数调用非常频繁的情况下。

    为什么需要内联函数?

    • 性能优化:在某些情况下,函数调用的开销可能很大,尤其是在循环中频繁调用的函数。内联函数可以避免这种开销,提高代码的执行效率。
    • 非局部返回:内联函数允许在 Lambda 表达式中使用 return 语句直接从包含它的函数返回,这在某些情况下非常有用。

    如何使用内联函数?

    • 在函数定义前加上 inline 关键字:
    CodeBlock Loading...
    • 这会让编译器尝试把 lock() 函数的代码直接嵌入到调用它的地方。

    内联函数的限制

    • 性能问题:虽然内联函数可以提高性能,但过多地使用内联函数可能会导致生成的代码体积变大。因此,应该适度使用内联函数,避免内联大型函数。
    • 非局部返回:在内联函数中,Lambda 表达式可以使用 return 语句直接从包含它的函数返回。这在某些情况下非常有用,但也可能导致代码逻辑变得复杂。

    noinline 关键字

    • 如果你只想内联部分 Lambda 表达式参数,可以使用 noinline 关键字:
    CodeBlock Loading...
    • inlined 参数会被内联,而 notInlined 不会。
    • noinline 类型的 Lambda 表达式可以像普通函数一样使用,可以存储在变量中或传递给其他函数。

    非局部返回

    • 在普通的 Lambda 表达式中,直接使用 return 是不允许的,因为 Lambda 表达式不能让外层函数返回。
    • 但是,如果 Lambda 表达式被内联了,就可以使用 return 直接从包含 Lambda 表达式的外层函数返回。这种称为非局部返回。
    CodeBlock Loading...

    crossinline 关键字

    • 如果内联函数在另一个执行上下文(比如局部对象或者嵌套函数)中调用 Lambda 表达式,那么 Lambda 表达式就不能使用非局部返回。使用 crossinline 关键字来强制约束:
    CodeBlock Loading...

    break 和 continue

    • 在内联函数中,Lambda 表达式可以使用 break 和 continue 来控制包含循环的控制流。

    reified 关键字

    • Kotlin 中,泛型在运行时会被擦除。也就是说无法在函数内部访问泛型的具体类型。
    • 使用 reified 关键字可以让你在内联函数中访问泛型类型,就像它是一个普通的类一样。例如:
    CodeBlock Loading...
    • 这样就可以使用 T::class、T is Type 等操作,无需反射。
    • 注意:只有内联函数才能使用 reified 关键字。

    内联属性

    • inline 关键字可以用于属性的 get() 和 set() 方法。可以内联整个属性,也可以单独内联 getter 或 setter。

    公共 API 内联函数的限制

    • 如果你的内联函数是公共 API 的一部分(public 或 protected),它会被其他模块调用,并且也会被内联。
    • 为了避免潜在的二进制兼容性问题,公共 API 内联函数不能使用非公共 API 的声明(private 或 internal)。
    • 你可以使用 @PublishedApi 注解来允许 internal 声明在公共 API 内联函数中使用。

    操作符重载

    核心概念

    • 操作符重载允许你为自定义类型定义操作符的行为。这使得你可以使用标准的操作符(如 +, -, *, / 等)来操作自定义类型,就像操作基本类型一样。

    如何实现操作符重载?

    • 在 Kotlin 中,操作符重载是通过在类中定义特定的函数来实现的。这些函数使用 operator 关键字标记。

    一元操作符

    操作符 对应的函数 例子 说明
    + unaryPlus() +a 正号 (通常不做任何改变)
    - unaryMinus() -a 取负数
    ! not() !a 逻辑非 (取反)

    示例

    CodeBlock Loading...

    自增/自减操作符 (++, --)

    • a++ (后置自增): 先返回 a 的原始值, 然后再对 a 进行自增。
    • ++a (前置自增): 先对 a 进行自增, 然后再返回 a 的新值。
    • a-- 和 --a 同理。

    • 重点: inc() 和 dec() 函数应该返回一个新值,赋值给变量,不要直接修改原始对象。

    二元操作符

    操作符 对应的函数 例子 说明
    + plus(b) a + b 加法
    - minus(b) a - b 减法
    * times(b) a * b 乘法
    / div(b) a / b 除法
    % rem(b) a % b 取余数 (求模)
    .. rangeTo(b) a..b 创建一个范围 (例如 1..10)
    ..< rangeUntil(b) a..<b 创建一个范围 (不包括最后一个元素)

    示例

    CodeBlock Loading...

    in 操作符

    • a in b: 检查 a 是否在 b 中。对应函数: b.contains(a)
    • a !in b: 检查 a 是否不在 b 中。对应函数: !b.contains(a)

    索引访问操作符 ([])

    • a[i]: 访问数组/集合中的元素。对应函数: a.get(i)
    • a[i] = b: 设置数组/集合中元素的值。对应函数: a.set(i, b)

    invoke 操作符 (())

    • a(): 像调用函数一样调用对象。对应函数: a.invoke()

    增强赋值操作符 (+=, -=, *=, /=, %=)

    • a += b: 相当于 a = a + b (如果 plusAssign 函数不存在)。优先使用 a.plusAssign(b)
    • 其他类似。

    相等和不等操作符 (==, !=)

    • a == b: 检查 a 和 b 是否相等。对应函数: a?.equals(b) ?: (b === null)
    • a != b: 检查 a 和 b 是否不相等。对应函数: !(a?.equals(b) ?: (b === null))

    比较操作符 (>, <, >=, <=)

    • a > b: 比较 a 和 b 的大小。对应函数: a.compareTo(b) > 0
    • 其他类似。

    注意

    • === 和 !== (检查是否是同一个对象) 不能重载。

    this 表达式

    核心概念

    • this 指的是 "当前对象"。就像你在跟别人说话时,用 "我" 来指代自己一样。

    this 的用法

    • 在类中:this 指的是当前类的对象。比如,在一个 Person 类中,this 指的就是当前这个 Person 对象。
    • 在扩展函数或带接收者的函数字面量中:this 指的是接收者参数,也就是点号 (.) 左边的那个对象。

    this 的指向问题

    如果只有一个 this,它通常指向最里层的作用域。但如果想要访问外部作用域的 this,就需要用到 限定的 this。

    限定的 this (Qualified this)

    当你想要从外部作用域 (比如外部类、扩展函数或带标签的函数字面量) 访问 this 时,可以使用 this@label 的形式。

    • @label 是作用域的标签。

    示例

    CodeBlock Loading...

    隐式的 this (Implicit this)

    当你在 this 对象上调用成员函数时,可以省略 this. 部分。

    注意

    如果你有一个同名的非成员函数,要小心使用 this,因为在某些情况下,可能会调用到非成员函数。

    示例

    CodeBlock Loading...

    注解 (Annotations)

    核心概念

    • 注解是一种给代码添加额外信息的方式,就像给代码贴标签。这些信息可以被编译器、工具或者运行时环境使用。

    如何声明注解?

    • 在 class 关键字前面加上 annotation 关键字。
    CodeBlock Loading...

    注解的元注解 (Meta-annotations)

    元注解是用来修饰注解的注解,用于指定注解的行为。

    • @Target:指定注解可以用于哪些代码元素上 (类、函数、属性等)。
    • @Retention:指定注解是否存储在编译后的 class 文件中,以及是否可以在运行时通过反射访问。默认情况下,两者都是 true。
    • @Repeatable:允许在同一个代码元素上多次使用同一个注解。
    • @MustBeDocumented:指定注解是公共 API 的一部分,应该包含在生成的 API 文档中。

    示例

    CodeBlock Loading...

    如何使用注解?

    CodeBlock Loading...

    注解构造函数

    • 注解可以有带参数的构造函数。
    CodeBlock Loading...

    允许的参数类型

    • Java 原始类型 (Int, Long 等)
    • 字符串 (String)
    • 类 (Foo::class)
    • 枚举 (Enum)
    • 其他注解
    • 以上类型的数组

    注意

    • 注解参数不能是可空类型,因为 JVM 不支持将 null 作为注解属性的值。

    注解作为参数

    • 如果一个注解被用作另一个注解的参数,它的名字不需要加 @ 符号。
    CodeBlock Loading...

    类作为参数

    • 如果需要将一个类作为注解的参数,使用 Kotlin 的 KClass。Kotlin 编译器会自动将其转换为 Java 类,以便 Java 代码可以正常访问注解和参数。
    CodeBlock Loading...

    实例化注解类

    • 在 Kotlin 中,你可以像调用普通类的构造函数一样调用注解类的构造函数,并使用生成的实例。
    CodeBlock Loading...

    Lambda 表达式上的注解

    • 注解可以用于 Lambda 表达式。它们会被应用到 Lambda 表达式生成的 invoke() 方法上。
    CodeBlock Loading...

    注解的使用位置 (Use-site Targets)

    • 当注解一个属性或主构造函数参数时,会生成多个 Java 元素,注解可以放在不同的位置。使用位置目标可以精确指定注解应该生成在哪个 Java 元素上。
    CodeBlock Loading...

    常见的使用位置目标

    • file:整个文件
    • property:属性 (Java 不可见)
    • field:字段
    • get:属性 getter
    • set:属性 setter
    • receiver:扩展函数或属性的接收者参数
    • param:构造函数参数
    • setparam:属性 setter 参数
    • delegate:委托属性的委托实例字段

    多个相同目标注解

    • 如果多个注解具有相同的使用位置目标,可以使用方括号将它们括起来,避免重复指定目标。
    CodeBlock Loading...

    Java 注解的兼容性

    • Kotlin 与 Java 注解 100% 兼容。

    Java 注解的参数

    • 由于 Java 注解的参数顺序未定义,不能使用常规函数调用语法传递参数。必须使用命名参数语法。
    CodeBlock Loading...

    value 参数的特殊情况

    • Java 中,如果注解只有一个名为 value 的参数,可以在 Kotlin 中省略参数名。
    CodeBlock Loading...

    数组作为注解参数

    • 如果 Java 中注解的参数类型是数组,在 Kotlin 中会变成 vararg 参数。
    CodeBlock Loading...
    • 对于其他数组类型的参数,需要使用数组字面量语法或 arrayOf(...)。
    CodeBlock Loading...

    访问注解实例的属性

    • 注解实例的值在 Kotlin 中作为属性暴露。
    CodeBlock Loading...

    可重复注解 (Repeatable Annotations)

    • Kotlin 支持可重复注解,即可以在同一个代码元素上多次使用同一个注解。使用 @kotlin.annotation.Repeatable 元注解标记注解声明,使其在 Kotlin 和 Java 中都可重复。
    CodeBlock Loading...
    • 可以使用 @kotlin.jvm.JvmRepeatable 元注解,并传递显式声明的包含注解类作为参数,为包含注解设置自定义名称。
    CodeBlock Loading...
    • 要通过反射提取 Kotlin 或 Java 可重复注解,使用 KAnnotatedElement.findAnnotations() 函数。

    解构

    核心概念

    • 解构声明是一种方便的语法,可以将一个对象拆解成多个变量,方便使用。

    语法

    CodeBlock Loading...

    原理:componentN() 函数

    • Kotlin 背后是通过调用一系列 componentN() 函数来实现解构的。
    • component1() 对应第一个变量
    • component2() 对应第二个变量
    • 以此类推,component3(),component4() 等等

    • 所以,只要一个对象有 componentN() 函数,就可以用解构声明。

    CodeBlock Loading...

    operator 关键字

    • componentN() 函数需要用 operator 关键字标记,才能用于解构声明。

    应用场景

    • for 循环:方便地从集合中提取元素。
    CodeBlock Loading...
    • 从函数返回多个值:使用 data class 可以方便地返回多个值,并且可以直接解构。
    CodeBlock Loading...
    • 遍历 Map:可以直接在 for 循环中解构 Map 的键值对。
    CodeBlock Loading...

    忽略不需要的变量:下划线 _

    • 如果解构时,你不需要某个变量,可以用下划线 _ 代替变量名,Kotlin 不会调用对应的 componentN() 函数,节省资源。
    CodeBlock Loading...

    Lambda 表达式中的解构

    • 可以在 Lambda 表达式的参数中使用解构声明,使代码更简洁。
    CodeBlock Loading...

    Lambda 中的类型声明

    • 可以为整个解构参数或单个组件指定类型。
    CodeBlock Loading...

    类引用、函数引用、属性引用、构造器引用,以及绑定引用

    Kotlin 提供了多种引用类型,允许我们在运行时操作程序的结构。理解这些引用类型对于编写更灵活、更强大的代码至关重要。

    类引用 (Class References): ::class

    • 作用:获取 Kotlin 类的运行时 KClass 对象。
    • 语法:ClassName::class
    • 类型:KClass<ClassName>
    • 用途:
      • 类型检查 (is 运算符)
      • 获取类的信息 (例如,名称、构造函数、属性等)
      • 与 Java 反射互操作 (.java 属性获取 java.lang.Class 对象)
    • 示例:
    CodeBlock Loading...

    函数引用 (Function References): ::

    • 作用:获取函数的引用,可以像函数类型的值一样使用。
    • 语法:::functionName
    • 类型:(ParameterTypes) -> ReturnType 或 KFunction<out R>
    • 用途:
      • 将函数作为参数传递给高阶函数 (例如,filter, map, reduce)
      • 创建函数组合
      • 动态调用函数
    • 示例:
    CodeBlock Loading...

    属性引用 (Property References): ::

    • 作用:获取属性的引用,可以访问属性的值和元数据。
    • 语法:::propertyName
    • 类型:
      • KProperty0<Type> (顶层属性或无接收者的属性)
      • KProperty1<Receiver, Type> (具有接收者的属性)
      • KMutableProperty... (可变属性)
    • 用途:
      • 动态获取和设置属性值
      • 访问属性的名称和类型
      • 用于高阶函数 (例如,map 可以提取对象的属性值)
    • 示例:
    CodeBlock Loading...

    构造器引用 (Constructor References): ::ClassName

    • 作用:获取构造函数的引用,可以动态创建类的实例。
    • 语法:::ClassName
    • 类型:(ConstructorParameterTypes) -> ClassName 或 KFunction<out ClassName>
    • 用途:
      • 依赖注入
      • 动态创建对象
      • 工厂模式
    • 示例:
    CodeBlock Loading...

    绑定引用 (Bound References): instance::member

    • 作用:将函数或属性绑定到特定的对象实例。
    • 语法:instance::memberName (其中 memberName 可以是函数或属性)
    • 类型:类型会根据绑定的成员和实例进行调整,不再包含接收者参数。
    • 用途:
      • 简化代码,避免每次都传递对象实例。
      • 创建状态相关的函数引用。
      • 在函数式编程中传递对象的特定行为。
    • 示例:
    CodeBlock Loading...

    核心区别总结

    特性 类引用 (::class) 函数引用 (::) 属性引用 (::) 构造器引用 (::ClassName) 绑定引用 (instance::member)
    引用对象 类 函数 属性 构造函数 实例 + 成员 (函数或属性)
    主要用途 类型信息、互操作 函数式编程 动态访问 动态创建对象 简化代码、状态相关
    绑定 不绑定 不绑定 不绑定 不绑定 绑定到特定对象实例

    选择合适的引用类型

    • 如果需要操作类本身的信息,使用类引用 (::class)。
    • 如果需要将函数作为参数传递或动态调用,使用函数引用 (::functionName)。
    • 如果需要动态访问属性值或获取属性元数据,使用属性引用 (::propertyName)。
    • 如果需要动态创建类的实例,使用构造器引用 (::ClassName)。
    • 如果需要将函数或属性绑定到特定对象实例,使用绑定引用 (instance::member)。
    KOTLIN
    data class Customer(val name: String, val email: String)
    
    KOTLIN
    fun foo(a: Int = 0, b: String = "") { ... }
    
    KOTLIN
    val positives = list.filter { x -> x > 0 }
    val positives = list.filter { it > 0 } // 更简洁
    
    KOTLIN
    if ("john@example.com" in emailsList) { ... }
    if ("jane@example.com" !in emailsList) { ... }
    
    KOTLIN
    println("Name $name")
    
    KOTLIN
    val wrongInt = readln().toIntOrNull()  // 如果输入无法转换为整数,则返回 null
    val correctInt = readln().toIntOrNull() // 如果输入可以转换为整数,则返回整数
    
    KOTLIN
    when (x) {
        is Foo -> ...
        is Bar -> ...
        else   -> ...
    }
    
    KOTLIN
    val list = listOf("a", "b", "c")
    
    KOTLIN
    val map = mapOf("a" to 1, "b" to 2, "c" to 3)
    
    KOTLIN
    println(map["key"])
    map["key"] = value  // 对于可变映射
    
    KOTLIN
    for ((k, v) in map) {
        println("$k -> $v")
    }
    
    KOTLIN
    for (i in 1..100) { ... }  // 闭区间:包括 100
    for (i in 1..<100) { ... } // 开区间:不包括 100
    for (x in 2..10 step 2) { ... } // 步长为 2
    for (x in 10 downTo 1) { ... } // 倒序
    (1..10).forEach { ... } // 使用 forEach
    
    KOTLIN
    val p: String by lazy { // 仅在第一次访问时计算该值
        // 计算字符串
    }
    
    KOTLIN
    fun String.spaceToCamelCase() { ... }
    
    "Convert this to camelcase".spaceToCamelCase()
    
    KOTLIN
    object Resource {
        val name = "Name"
    }
    
    KOTLIN
    @JvmInline
    value class EmployeeId(private val id: String)
    
    @JvmInline
    value class CustomerId(private val id: String)
    
    KOTLIN
    abstract class MyAbstractClass {
        abstract fun doSomething()
        abstract fun sleep()
    }
    
    fun main() {
        val myObject = object : MyAbstractClass() {
            override fun doSomething() {
                // ...
            }
    
            override fun sleep() { // ...
            }
        }
        myObject.doSomething()
    }
    
    KOTLIN
    val files = File("Test").listFiles()
    
    println(files?.size) // 如果 files 不为空,则打印 size
    
    KOTLIN
    val files = File("Test").listFiles()
    
    println(files?.size ?: "empty") // 如果 files 为 null,则打印 "empty"
    
    val filesSize = files?.size ?: run {
        val someSize = getSomeSize()
        someSize * 2
    }
    println(filesSize)
    
    KOTLIN
    val values = ...
    val email = values["email"] ?: throw IllegalStateException("Email is missing!")
    
    KOTLIN
    val emails = ... // 可能为空
    val mainEmail = emails.firstOrNull() ?: ""
    
    KOTLIN
    val value = ...
    
    value?.let {
        ... // 如果 value 不为空,则执行此代码块
    }
    
    KOTLIN
    val value = ...
    
    val mapped = value?.let { transformValue(it) } ?: defaultValue
    // 如果 value 或 transform 结果为空,则返回 defaultValue。
    
    KOTLIN
    fun transform(color: String): Int {
        return when (color) {
            "Red" -> 0
            "Green" -> 1
            "Blue" -> 2
            else -> throw IllegalArgumentException("Invalid color param value")
        }
    }
    
    KOTLIN
    fun test() {
        val result = try {
            count()
        } catch (e: ArithmeticException) {
            throw IllegalStateException(e)
        }
    
        // 使用 result
    }
    
    KOTLIN
    val y = if (x == 1) {
        "one"
    } else if (x == 2) {
        "two"
    } else {
        "other"
    }
    
    KOTLIN
    fun arrayOfMinusOnes(size: Int): IntArray {
        return IntArray(size).apply { fill(-1) }
    }
    
    KOTLIN
    fun theAnswer() = 42
    
    // 等价于
    
    fun theAnswer(): Int {
        return 42
    }
    
    fun transform(color: String): Int = when (color) {
        "Red" -> 0
        "Green" -> 1
        "Blue" -> 2
        else -> throw IllegalArgumentException("Invalid color param value")
    }
    
    KOTLIN
    class Turtle {
        fun penDown()
        fun penUp()
        fun turn(degrees: Double)
        fun forward(pixels: Double)
    }
    
    val myTurtle = Turtle()
    with(myTurtle) { // 画一个 100 像素的正方形
        penDown()
        for (i in 1..4) {
            forward(100.0)
            turn(90.0)
        }
        penUp()
    }
    
    KOTLIN
    val myRectangle = Rectangle().apply {
        length = 4
        breadth = 5
        color = 0xFAFAFA
    }
    
    KOTLIN
    val stream = Files.newInputStream(Paths.get("/some/file.txt"))
    stream.buffered().reader().use { reader ->
        println(reader.readText())
    }
    
    KOTLIN
    inline fun <reified T: Any> Gson.fromJson(json: JsonElement): T = this.fromJson(json, T::class.java)
    
    KOTLIN
    var a = 1
    var b = 2
    a = b.also { b = a }
    
    KOTLIN
    val age: Int = 30
    val name = "Alice"  // 类型推断为 String
    val pi = 3.14        // 类型推断为 Double
    
    KOTLIN
    val max = if (a > b) a else b  // 返回 a 或 b 中较大的值
    
    KOTLIN
    when (x) {
        1 -> println("x is 1")
        in 2..5 -> println("x is between 2 and 5")
        is String -> println("x is a String")
        else -> println("x is something else")
    }
    
    KOTLIN
    val numbers = listOf(1, 2, 3)
    for (number in numbers) {
        println(number)
    }
    
    for (i in 1..5) { // 1 到 5 (包含 5)
        println(i)
    }
    
    KOTLIN
    var count = 0
    while (count < 5) {
        println("Count: $count")
        count++
    }
    
    var input: String
    do {
        print("Enter a number (or 'q' to quit): ")
        input = readLine() ?: "" // 安全处理空输入
    } while (input != "q")
    
    KOTLIN
    for (i in 1..10) {
        if (i == 3) continue // 跳过 i=3 的迭代
        if (i == 7) break    // 循环在 i=7 时终止
        println(i)
    }
    
    KOTLIN
    loop@ for (i in 1..100) {
        for (j in 1..100) {
            if (...) break@loop // 退出标记为 loop 的循环
        }
    }
    
    KOTLIN
    fun foo() {
        listOf(1, 2, 3, 4, 5).forEach lit@{
            if (it == 3) return@lit // 局部返回到 forEach 循环的调用者
            print(it)
        }
        print(" done with explicit label")
    }
     
    KOTLIN
    fun foo() {
        listOf(1, 2, 3, 4, 5).forEach {
            if (it == 3) return@forEach // 局部返回到 forEach 循环的调用者
            print(it)
        }
        print(" done with implicit label")
    }
     
    KOTLIN
    fun foo() {
        listOf(1, 2, 3, 4, 5).forEach(fun(value: Int) {
            if (value == 3) return  // 局部返回到匿名函数的调用者
            print(value)
        })
        print(" done with anonymous function")
    }
     
    KOTLIN
    fun foo() {
        run loop@{
            listOf(1, 2, 3, 4, 5).forEach {
                if (it == 3) return@loop // 从传递给 run 的 lambda 表达式非局部返回
                print(it)
            }
        }
        print(" done with nested loop")
    }
    
    KOTLIN
    class Example // 隐式继承自 Any
    
    KOTLIN
    open class Base // 类可以被继承
    
    KOTLIN
    open class Base(p: Int)
    
    class Derived(p: Int) : Base(p)
    
    KOTLIN
    class MyView : View {
        constructor(ctx: Context) : super(ctx)
    
        constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs)
    }
    
    KOTLIN
    open class Shape {
        open fun draw() { /*...*/ }
        fun fill() { /*...*/ }
    }
    
    class Circle() : Shape() {
        override fun draw() { /*...*/ }
    }
    
    KOTLIN
    open class Rectangle() : Shape() {
        final override fun draw() { /*...*/ }
    }
    
    KOTLIN
    open class Shape {
        open val vertexCount: Int = 0
    }
    
    class Rectangle : Shape() {
        override val vertexCount = 4
    }
    
    KOTLIN
    interface Shape {
        val vertexCount: Int
    }
    
    class Rectangle(override val vertexCount: Int = 4) : Shape // 始终有 4 个顶点
    
    class Polygon : Shape {
        override var vertexCount: Int = 0  // 稍后可以设置为任何数字
    }
    
    KOTLIN
    open class Base(val name: String) {
    
        init { println("Initializing a base class") }
    
        open val size: Int =
            name.length.also { println("Initializing size in the base class: $it") }
    }
    
    class Derived(
        name: String,
        val lastName: String,
    ) : Base(name.replaceFirstChar { it.uppercase() }.also { println("Argument for the base class: $it") }) {
    
        init { println("Initializing a derived class") }
    
        override val size: Int =
            (super.size + lastName.length).also { println("Initializing size in the derived class: $it") }
    }
    
    KOTLIN
    open class Rectangle {
        open fun draw() { println("Drawing a rectangle") }
        val borderColor: String get() = "black"
    }
    
    class FilledRectangle : Rectangle() {
        override fun draw() {
            super.draw()
            println("Filling the rectangle")
        }
    
        val fillColor: String get() = super.borderColor
    }
    
    KOTLIN
    class FilledRectangle: Rectangle() {
        override fun draw() {
            val filler = Filler()
            filler.drawAndFill()
        }
    
        inner class Filler {
            fun fill() { println("Filling") }
            fun drawAndFill() {
                super@FilledRectangle.draw() // 调用 Rectangle.draw()
                fill()
                println("Drawn a filled rectangle with color ${super@FilledRectangle.borderColor}") // 使用 Rectangle 的 borderColor 的 get() 实现
            }
        }
    }
    
    KOTLIN
    open class Rectangle {
        open fun draw() { /* ... */ }
    }
    
    interface Polygon {
        fun draw() { /* ... */ } // 接口成员默认是 'open' 的
    }
    
    class Square() : Rectangle(), Polygon {
        // 编译器要求重写 draw():
        override fun draw() {
            super<Rectangle>.draw() // 调用 Rectangle.draw()
            super<Polygon>.draw() // 调用 Polygon.draw()
        }
    }
    
    KOTLIN
    class Box<T>(t: T) {
        var value = t
    }
    
    KOTLIN
    val box: Box<Int> = Box<Int>(1) // 显式指定类型
    val box = Box(1) // 编译器推断类型为 Box<Int>
    
    KOTLIN
    interface Source<out T> {
        fun nextT(): T
    }
    
    fun demo(strs: Source<String>) {
        val objects: Source<Any> = strs // This is OK, since T is an out-parameter
        // ...
    }
    
    KOTLIN
    interface Comparable<in T> {
        operator fun compareTo(other: T): Int
    }
    
    fun demo(x: Comparable<Number>) {
        x.compareTo(1.0) // 1.0 has type Double, which is a subtype of Number
        // Thus, you can assign x to a variable of type Comparable<Double>
        val y: Comparable<Double> = x // OK!
    }
    
    KOTLIN
    fun copy(from: Array<out Any>, to: Array<Any>) { ... } // 禁止写入 from
    fun fill(dest: Array<in String>, value: String) { ... } // 禁止读取 dest
    
    KOTLIN
    fun <T> singletonList(item: T): List<T> { ... }
    fun <T> T.basicToString(): String { ... } // 扩展函数
    
    KOTLIN
    val l = singletonList<Int>(1) // 显式指定类型
    val l = singletonList(1) // 编译器推断类型
    
    KOTLIN
    fun <T : Comparable<T>> sort(list: List<T>) { ... } // T 必须是 Comparable<T> 的子类型
    
    KOTLIN
    fun <T> copyWhenGreater(list: List<T>, threshold: T): List<String>
        where T : CharSequence,
              T : Comparable<T> {
        return list.filter { it > threshold }.map { it.toString() }
    } // T 必须同时实现 CharSequence 和 Comparable
    
    KOTLIN
    interface ArcadeGame<T1> : Game<T1> {
        override fun save(x: T1): T1
        override fun load(x: T1 & Any): T1 & Any // T1 是明确的非空类型
    }
    
    KOTLIN
    data class User(val name: String, val age: Int)
    
    KOTLIN
    data class User(val name: String = "", val age: Int = 0)
    
    KOTLIN
    data class Person(val name: String) {
        var age: Int = 0
    }
    
    KOTLIN
    val person1 = Person("John")
    val person2 = Person("John")
    person1.age = 10
    person2.age = 20
    
    println("person1 == person2: ${person1 == person2}")
    // person1 == person2: true
    
    println("person1 with age ${person1.age}: ${person1}")
    // person1 with age 10: Person(name=John)
    
    println("person2 with age ${person2.age}: ${person2}")
    // person2 with age 20: Person(name=John)
    
    KOTLIN
    fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
    
    KOTLIN
    val jack = User(name = "Jack", age = 1)
    val olderJack = jack.copy(age = 2)
    
    KOTLIN
    val jane = User("Jane", 35)
    val (name, age) = jane
    println("$name, $age years of age")
    // Jane, 35 years of age
    
    KOTLIN
    enum class Direction {
        NORTH, SOUTH, WEST, EAST
    }
    
    KOTLIN
    enum class Color(val rgb: Int) {
        RED(0xFF0000),
        GREEN(0x00FF00),
        BLUE(0x0000FF)
    }
    
    KOTLIN
    enum class ProtocolState {
        WAITING {
            override fun signal() = TALKING
        },
    
        TALKING {
            override fun signal() = WAITING
        };
    
        abstract fun signal(): ProtocolState
    }
    
    KOTLIN
    enum class IntArithmetics : BinaryOperator<Int>, IntBinaryOperator {
        PLUS {
            override fun apply(t: Int, u: Int): Int = t + u
        },
        TIMES {
            override fun apply(t: Int, u: Int): Int = t * u
        };
    
        override fun applyAsInt(t: Int, u: Int) = apply(t, u)
    }
    
    KOTLIN
    enum class RGB { RED, GREEN, BLUE }
    
    fun main() {
      for (color in RGB.entries) println(color.toString()) // prints RED, GREEN, BLUE
      println("The first color is: ${RGB.valueOf("RED")}") // prints "The first color is: RED"
    }
    
    KOTLIN
    println(RGB.RED.name)    // prints RED
    println(RGB.RED.ordinal) // prints 0
    
    KOTLIN
    enum class RGB { RED, GREEN, BLUE }
    
    inline fun <reified T : Enum<T>> printAllValues() {
        println(enumEntries<T>().joinToString { it.name })
    }
    
    printAllValues<RGB>()
    // RED, GREEN, BLUE
    
    KOTLIN
    @JvmInline
    value class Password(val value: String)
    
    KOTLIN
    @JvmInline
    value class Email(val value: String)
    
    fun sendEmail(email: String) { // 接收普通字符串
    }
    
    fun sendEmail(email: Email) { // 接收 Email 类型
    }
    
    val myEmail: Email = Email("test@example.com")
    val myString: String = "test@example.com"
    
    sendEmail(myEmail) // 调用接收 Email 类型的函数
    sendEmail(myString) // 调用接收普通字符串类型的函数
    
    KOTLIN
    object MySingleton {
        val name = "Singleton"
    
        fun doSomething() {
            println("Doing something in the singleton")
        }
    }
    
    fun main() {
        println(MySingleton.name) // 访问单例对象的属性
        MySingleton.doSomething()  // 调用单例对象的方法
    }
    
    KOTLIN
    class MyClass {
        companion object {
            val constant = "Class Constant"
    
            fun create(): MyClass {
                return MyClass()
            }
        }
    }
    
    fun main() {
        println(MyClass.constant)  // 访问伴生对象的属性
        val instance = MyClass.create() // 调用伴生对象的方法
    }
    
    KOTLIN
    interface MyInterface {
        fun doSomething()
    }
    
    fun main() {
        val myObject = object : MyInterface {
            override fun doSomething() {
                println("Doing something in the anonymous object")
            }
        }
    
        myObject.doSomething()
    }
    
    KOTLIN
    import org.example.Message
    import org.test.Message as TestMessage  // 现在可以用 TestMessage 代替 org.test.Message
    
    KOTLIN
    interface MyInterface {
        fun bar() // 抽象方法,必须在实现类中实现
        fun foo() { // 带有默认实现的方法,实现类可以选择重写
            // 可选的方法体
        }
    }
    
    KOTLIN
    class Child : MyInterface {
        override fun bar() { // 必须实现 MyInterface 中的抽象方法 bar()
            // 方法体
        }
    }
    
    KOTLIN
    interface MyInterface {
        val prop: Int // 抽象属性,必须在实现类中实现
    
        val propertyWithImplementation: String
            get() = "foo" // 带有默认 getter 实现的属性
    
        fun foo() {
            print(prop)
        }
    }
    
    class Child : MyInterface {
        override val prop: Int = 29 // 必须实现 MyInterface 中的抽象属性 prop
    }
    
    KOTLIN
    interface Named {
        val name: String
    }
    
    interface Person : Named {
        val firstName: String
        val lastName: String
    
        override val name: String get() = "$firstName $lastName" // 提供了 name 属性的默认实现
    }
    
    data class Employee(
        override val firstName: String,
        override val lastName: String,
        val position: Position
    ) : Person // 不需要实现 name 属性,因为 Person 接口已经提供了默认实现
    
    KOTLIN
    interface A {
        fun foo() { print("A") }
        fun bar()
    }
    
    interface B {
        fun foo() { print("B") }
        fun bar() { print("bar") }
    }
    
    class C : A {
        override fun bar() { print("bar") }
    }
    
    class D : A, B {
        override fun foo() {
            super<A>.foo() // 调用 A 接口的 foo() 实现
            super<B>.foo() // 调用 B 接口的 foo() 实现
        }
    
        override fun bar() {
            super<B>.bar() // 调用 B 接口的 bar() 实现
        }
    }
    
    KOTLIN
    fun interface KRunnable {
        fun invoke() // 唯一的抽象方法
    }
    
    KOTLIN
    fun interface IntPredicate {
        fun accept(i: Int): Boolean // 唯一的抽象方法
    }
    
    // 不使用 SAM 转换 (传统方式)
    val isEven = object : IntPredicate {
        override fun accept(i: Int): Boolean {
            return i % 2 == 0
        }
    }
    
    // 使用 SAM 转换 (lambda 表达式)
    val isEvenLambda: IntPredicate = IntPredicate { it % 2 == 0 }
    
    fun main() {
        println("Is 7 even? - ${isEven.accept(7)}") // 输出: Is 7 even? - false
        println("Is 7 even? - ${isEvenLambda.accept(7)}") // 输出: Is 7 even? - false
    }
    
    KOTLIN
    interface Printer {
        fun print()
    }
    
    fun Printer(block: () -> Unit): Printer = object : Printer {
        override fun print() = block()
    }
    
    // 可以替换为:
    
    fun interface Printer {
        fun print()
    }
    
    // 使用函数引用
    // documentsStorage.addPrinter(::Printer)
    
    KOTLIN
    @Deprecated(
        message = "使用函数式接口 Printer 代替",
        level = DeprecationLevel.HIDDEN
    )
    fun Printer(...) {...}
    
    KOTLIN
    typealias IntPredicate = (i: Int) -> Boolean // IntPredicate 仅仅是函数类型 (i: Int) -> Boolean 的别名
      
    KOTLIN
    fun interface IntPredicate {
        fun accept(i: Int): Boolean // IntPredicate 是一个真实的接口类型
    }
      
    KOTLIN
    // 文件名: example.kt
    package foo
    
    private fun foo() { ... } // 只在 example.kt 文件内可见
    
    public var bar: Int = 5 // 在任何地方都可见
        private set         // setter 只在 example.kt 文件内可见
    
    internal val baz = 6    // 在同一模块内可见
    
    KOTLIN
    open class Outer {
        private val a = 1 // 只在 Outer 类内部可见
        protected open val b = 2 // 在 Outer 类及其子类中可见
        internal open val c = 3 // 在同一模块内可见
        val d = 4  // public (默认) 在任何地方都可见
    
        protected class Nested {
            public val e: Int = 5
        }
    }
    
    class Subclass : Outer() {
        // a 不可见
        // b, c 和 d 可见
        // Nested 和 e 可见
    
        override val b = 5   // 'b' 是 protected
        override val c = 7   // 'c' 是 internal
    }
    
    class Unrelated(o: Outer) {
        // o.a, o.b 不可见
        // o.c 和 o.d 可见 (同一模块)
        // Outer.Nested 不可见,Nested::e 也不可见
    }
    
    KOTLIN
    class C private constructor(a: Int) { ... } // 构造函数是 private
    
    KOTLIN
    var <属性名>: <类型> by <表达式>
    
    KOTLIN
    operator fun getValue(thisRef: Any?, property: KProperty<*>): 返回值类型 { ... }
      
    KOTLIN
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: 新值类型) { ... }
      
    KOTLIN
    val lazyValue: String by lazy {
        println("Computed!")
        "Hello"
    }
    
    fun main() {
        println(lazyValue) // 输出: Computed! \n Hello
        println(lazyValue) // 输出: Hello
    }
      
    KOTLIN
    import kotlin.properties.Delegates
    
    class User {
        var name: String by Delegates.observable("<no name>") {
            prop, old, new ->
            println("Property ${prop.name} changed: $old -> $new")
        }
    }
    
    fun main() {
        val user = User()
        user.name = "first"  // 输出: Property name changed: <no name> -> first
        user.name = "second" // 输出: Property name changed: first -> second
    }
      
    KOTLIN
    import kotlin.properties.Delegates
    
    class PositiveValueHolder {
        var value: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
            newValue >= 0 // Only allow non-negative values
        }
    }
    
    fun main() {
        val holder = PositiveValueHolder()
        holder.value = 10   // OK
        println(holder.value) // Prints 10
    
        holder.value = -5   // Vetoed! Value remains 10
        println(holder.value) // Prints 10
    }
      
    KOTLIN
    var topLevelInt: Int = 0
    
    class ClassWithDelegate(val anotherClassInt: Int)
    
    class MyClass(var memberInt: Int, val anotherClassInstance: ClassWithDelegate) {
        var delegatedToMember: Int by this::memberInt
        var delegatedToTopLevel: Int by ::topLevelInt
    
        val delegatedToAnotherClass: Int by anotherClassInstance::anotherClassInt
    }
    
    var MyClass.extDelegated: Int by ::topLevelInt
    
    class MyClass2 {
        var newName: Int = 0
    
        @Deprecated("Use 'newName' instead", ReplaceWith("newName"))
        var oldName: Int by this::newName
    }
    
    KOTLIN
    class User(val map: Map<String, Any?>) {
        val name: String by map
        val age: Int     by map
    }
    
    fun main() {
        val user = User(mapOf(
            "name" to "John Doe",
            "age"  to 25
        ))
    
        println(user.name) // 输出: John Doe
        println(user.age)  // 输出: 25
    }
    
    KOTLIN
    fun processData(data: String?) {
        val processedData by lazy {
            println("Processing data...")
            data?.uppercase() ?: "Default Value"
        }
    
        if (data != null && data.isNotEmpty()) {
            println("Using processed data: $processedData") // Triggers lazy initialization if 'data' is not null/empty
        } else {
            println("No data to process.")
        }
    }
    
    KOTLIN
    operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): PropertyDelegate { ... }
    
    KOTLIN
    import kotlin.properties.ReadOnlyProperty
    import kotlin.reflect.KProperty
    
    class ResourceDelegate<T>(private val initializer: () -> T) : ReadOnlyProperty<Any?, T> {
        private var value: T? = null
    
        override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            if (value == null) {
                value = initializer()
            }
            return value!!
        }
    }
    
    class ResourceLoader<T>(private val id: String) {
        operator fun provideDelegate(thisRef: Any?, property: KProperty<*>): ReadOnlyProperty<Any?, T> {
            println("Binding resource ${property.name} with ID $id")
            // Perform validation or customization here, e.g., check if resource ID is valid
    
            return ResourceDelegate {
                println("Initializing resource ${property.name} with ID $id")
                // Load resource based on ID (replace with actual resource loading logic)
                "Loaded Resource for ${property.name} (ID: $id)" as T // Type cast is necessary as T is only known at compile time.
            }
        }
    }
    
    class MyUI {
        val image: String by ResourceLoader("image_id")
        val text: String by ResourceLoader("text_id")
    }
    
    fun main() {
        val ui = MyUI()
        println(ui.image)
        println(ui.text)
    }
    
    KOTLIN
    class C {
        private val prop$delegate = MyDelegate()
        var prop: Type
            get() = prop$delegate.getValue(this, this::prop)
            set(value: Type) = prop$delegate.setValue(this, this::prop, value)
    }
     
    KOTLIN
    fun MutableList<Int>.swap(index1: Int, index2: Int) {
        val tmp = this[index1] // 'this' 指代列表
        this[index1] = this[index2]
        this[index2] = tmp
    }
    
    KOTLIN
    val list = mutableListOf(1, 2, 3)
    list.swap(0, 2) // 'this' 在 'swap()' 内部将保存 'list' 的值
    
    KOTLIN
    open class Shape
    class Rectangle: Shape()
    
    fun Shape.getName() = "Shape"
    fun Rectangle.getName() = "Rectangle"
    
    fun printClassName(s: Shape) {
        println(s.getName())
    }
    
    printClassName(Rectangle()) // 输出: Shape,因为 getName() 是根据 s 的声明类型 Shape 调用的
    
    KOTLIN
    class Example {
        fun printFunctionType() { println("Class method") }
    }
    
    fun Example.printFunctionType() { println("Extension function") }
    
    Example().printFunctionType() // 输出: Class method
    
    KOTLIN
    class Example {
        fun printFunctionType() { println("Class method") }
    }
    
    fun Example.printFunctionType(i: Int) { println("Extension function #$i") }
    
    Example().printFunctionType(1) // 输出: Extension function #1
    
    KOTLIN
    fun Any?.toString(): String {
        if (this == null) return "null"
        // 空检查后,'this' 自动转换为非空类型,因此 toString() 解析为 Any 类的成员函数
        return toString()
    }
    
    KOTLIN
    val <T> List<T>.lastIndex: Int
        get() = size - 1
    
    KOTLIN
    class House
    val House.number = 1 // 错误:不允许对扩展属性进行初始化
    
    KOTLIN
    class MyClass {
        companion object { }  // 将被称为 "Companion"
    }
    
    fun MyClass.Companion.printCompanion() { println("companion") }
    
    fun main() {
        MyClass.printCompanion() // 输出: companion
    }
    
    KOTLIN
    package org.example.declarations
    
    fun List<String>.getLongestString() { /*...*/ }
    
    KOTLIN
    package org.example.usage
    
    import org.example.declarations.getLongestString
    
    fun main() {
        val list = listOf("red", "green", "blue")
        list.getLongestString()
    }
    
    KOTLIN
    class Host(val hostname: String) {
        fun printHostname() { print(hostname) }
    }
    
    class Connection(val host: Host, val port: Int) {
        fun printPort() { print(port) }
    
        fun Host.printConnectionString() {
            printHostname()   // 调用 Host.printHostname()
            print(":")
            printPort()   // 调用 Connection.printPort()
        }
    
        fun connect() {
            /*...*/
            host.printConnectionString()   // 调用扩展函数
        }
    }
    
    fun main() {
        Connection(Host("kotl.in"), 443).connect() //Host("kotl.in").printConnectionString()  // 错误,扩展函数在 Connection 外部不可用
    }
    
    KOTLIN
    class Connection {
        fun Host.getConnectionString() {
            toString()         // 调用 Host.toString()
            this@Connection.toString()  // 调用 Connection.toString()
        }
    }
    
    KOTLIN
    open class Base {
        open fun Base.printFunctionInfo() {
            println("Base extension function in BaseCaller")
        }
    
        open fun Derived.printFunctionInfo() {
            println("Derived extension function in BaseCaller")
        }
    }
    
    class Derived : Base() {
        override fun Base.printFunctionInfo() {
            println("Base extension function in DerivedCaller")
        }
    
        override fun Derived.printFunctionInfo() {
            println("Derived extension function in DerivedCaller")
        }
    }
    
    fun main() {
        BaseCaller().call(Base())   // "Base extension function in BaseCaller"
        DerivedCaller().call(Base())  // "Base extension function in DerivedCaller" - 分发接收者是虚拟解析的
        DerivedCaller().call(Derived())  // "Base extension function in DerivedCaller" - 扩展接收者是静态解析的
    }
    
    KOTLIN
    // 简化集合类型
    typealias NodeSet = Set<Network.Node>
    
    // 简化泛型 Map 类型
    typealias FileTable<K> = MutableMap<K, MutableList<File>>
    
    // 简化函数类型
    typealias MyHandler = (Int, String, Any) -> Unit
    typealias Predicate<T> = (T) -> Boolean
    
    // 简化内部类
    class A { inner class Inner }
    class B { inner class Inner }
    typealias AInner = A.Inner
    typealias BInner = B.Inner
    
    KOTLIN
    typealias Predicate<T> = (T) -> Boolean
    
    fun foo(p: Predicate<Int>) = p(42) // 函数需要一个 Predicate<Int> 类型的参数
    
    fun main() {
        val f: (Int) -> Boolean = { it > 0 } //  f 是一个 (Int) -> Boolean 类型的变量
        println(foo(f)) // 可以直接把 f 传给 foo,因为 Predicate<Int> 和 (Int) -> Boolean 本质上是一样的
    
        val p: Predicate<Int> = { it > 0 } // p 是一个 Predicate<Int> 类型的变量
        println(listOf(1, -2).filter(p)) // 可以用 p 来过滤列表,效果和直接用 (Int) -> Boolean 一样
    }
    
    KOTLIN
    fun double(x: Int): Int {
        return 2 * x
    }
    
    KOTLIN
    val result = double(2) // 调用 double 函数
    Stream().read()       // 创建 Stream 类的实例并调用 read 函数
    
    KOTLIN
    fun powerOf(number: Int, exponent: Int): Int { /* 函数体 */ }
    
    KOTLIN
    fun powerOf(
        number: Int,
        exponent: Int, // 尾随逗号
    ) { /* 函数体 */ }
    
    KOTLIN
    fun read(
        b: ByteArray,
        off: Int = 0,
        len: Int = b.size,
    ) { /* 函数体 */ }
    
    KOTLIN
    open class A {
        open fun foo(i: Int = 10) { /*...*/ }
    }
    
    class B : A() {
        override fun foo(i: Int) { /*...*/ }  // 不允许设置默认值.
    }
    
    KOTLIN
    fun foo(
        bar: Int = 0,
        baz: Int,
    ) { /*...*/ }
    
    foo(baz = 1) // 使用默认值 bar = 0
    
    KOTLIN
    fun foo(
        bar: Int = 0,
        baz: Int = 1,
        qux: () -> Unit,
    ) { /*...*/ }
    
    foo(1) { println("hello") }     // 使用默认值 baz = 1
    foo(qux = { println("hello") }) // 使用默认值 bar = 0 和 baz = 1
    foo { println("hello") }        // 使用默认值 bar = 0 和 baz = 1
    
    KOTLIN
    fun reformat(
        str: String,
        normalizeCase: Boolean = true,
        upperCaseFirstLetter: Boolean = true,
        divideByCamelHumps: Boolean = false,
        wordSeparator: Char = ' ',
    ) { /*...*/ }
    
    KOTLIN
    reformat(
        "String!",
        false,
        upperCaseFirstLetter = false,
        divideByCamelHumps = true,
        '_'
    )
    
    KOTLIN
    reformat("This is a long String!")
    
    KOTLIN
    reformat("This is a short String!", upperCaseFirstLetter = false, wordSeparator = '_')
    
    KOTLIN
    fun foo(vararg strings: String) { /*...*/ }
    
    foo(strings = *arrayOf("a", "b", "c"))
    
    KOTLIN
    fun printHello(name: String?): Unit {
        if (name != null)
            println("Hello $name")
        else
            println("Hi there!")
        // `return Unit` 或 `return` 是可选的
    }
    
    KOTLIN
    fun printHello(name: String?) { ... }
    
    KOTLIN
    fun double(x: Int): Int = x * 2
    
    KOTLIN
    fun double(x: Int) = x * 2
    
    KOTLIN
    fun <T> asList(vararg ts: T): List<T> {
        val result = ArrayList<T>()
        for (t in ts) // ts 是一个 Array
            result.add(t)
        return result
    }
    
    KOTLIN
    val list = asList(1, 2, 3)
    
    KOTLIN
    val a = arrayOf(1, 2, 3)
    val list = asList(-1, 0, *a, 4)
    
    KOTLIN
    val a = intArrayOf(1, 2, 3) // IntArray 是一个原始类型数组
    val list = asList(-1, 0, *a.toTypedArray(), 4)
    
    KOTLIN
    infix fun Int.shl(x: Int): Int { /* 函数体 */ }
    
    // 使用中缀符号调用函数
    1 shl 2
    
    // 与以下相同
    1.shl(2)
    
    KOTLIN
    class MyStringCollection {
        infix fun add(s: String) { /*...*/ }
    
        fun build() {
            this add "abc"   // Correct
            add("abc")       // Correct
            //add "abc"        // Incorrect: the receiver must be specified
        }
    }
    
    KOTLIN
    fun dfs(graph: Graph) {
        fun dfs(current: Vertex, visited: MutableSet<Vertex>) {
            if (!visited.add(current)) return
            for (v in current.neighbors)
                dfs(v, visited)
        }
    
        dfs(graph.vertices[0], HashSet())
    }
    
    KOTLIN
    fun dfs(graph: Graph) {
        val visited = HashSet<Vertex>()
        fun dfs(current: Vertex) {
            if (!visited.add(current)) return
            for (v in current.neighbors)
                dfs(v)
        }
    
        dfs(graph.vertices[0])
    }
    
    KOTLIN
    class Sample {
        fun foo() { print("Foo") }
    }
    
    KOTLIN
    Sample().foo() // 创建 Sample 类的实例并调用 foo
    
    KOTLIN
    fun <T> singletonList(item: T): List<T> { /*...*/ }
    
    KOTLIN
    val eps = 1E-10 // "足够好", 可以是 10^-15
    
    tailrec fun findFixPoint(x: Double = 1.0): Double =
        if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))
    
    KOTLIN
    fun <T, R> Collection<T>.fold(
        initial: R,                // 初始值
        combine: (acc: R, nextElement: T) -> R  // 组合函数
    ): R {
        var accumulator: R = initial
        for (element: T in this) {
            accumulator = combine(accumulator, element) // 调用组合函数
        }
        return accumulator
    }
      
    KOTLIN
    val numbers = listOf(1, 2, 3)
    
    // 使用 Lambda 表达式 (最常见)
    val sum = numbers.fold(0) { acc, number -> acc + number } // acc是累加值,number是集合中的元素
    println(sum) // 输出:6
    
    // 使用函数引用
    val product = numbers.fold(1, Int::times) // Int::times 是一个函数引用,指向 Int 类的 times 函数
    println(product) // 输出:6
      
    KOTLIN
    typealias MyHandler = (String, Int) -> Unit
    
    KOTLIN
    class IntTransformer: (Int) -> Int {
        override operator fun invoke(x: Int): Int = TODO() // 实现 invoke 方法
    }
    
    KOTLIN
    var sum = 0
    numbers.forEach { sum += it }
    println(sum) // 输出:numbers所有元素的总和
    
    KOTLIN
    val sum: Int.(Int) -> Int = { other -> this + other }
    println(1.sum(2)) // 输出:3
    
    KOTLIN
    inline fun <T> lock(lock: Lock, body: () -> T): T { ... }
    
    KOTLIN
    inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { ... }
    
    KOTLIN
    fun foo() {
        inlined {
            return // OK: the lambda is inlined, 直接从 foo() 返回
        }
    }
    
    KOTLIN
    inline fun f(crossinline body: () -> Unit) {
        val f = object: Runnable {
            override fun run() = body() // 这里调用 body(),所以不能使用非局部返回
        }
    }
    
    KOTLIN
    inline fun <reified T> TreeNode.findParentOfType(): T? { ... }
    
    KOTLIN
    data class Point(val x: Int, val y: Int)
    
    operator fun Point.unaryMinus() = Point(-x, -y) // 重载了负号操作符
    
    val point = Point(10, 20)
    
    fun main() {
       println(-point)  // 输出 "Point(x=-10, y=-20)"
    }
    
    KOTLIN
    data class Counter(val dayIndex: Int) {
        operator fun plus(increment: Int): Counter {
            return Counter(dayIndex + increment)
        }
    }
    
    KOTLIN
    class A { // 隐式标签 @A
        inner class B { // 隐式标签 @B
            fun Int.foo() { // 隐式标签 @foo
                val a = this@A // A 的 this (A 类的对象)
                val b = this@B // B 的 this (B 类的对象)
    
                val c = this // foo() 的接收者, 一个 Int (当前的 Int 值)
                val c1 = this@foo // foo() 的接收者, 一个 Int (当前的 Int 值)
    
                val funLit = lambda@ fun String.() {
                    val d = this // funLit 的接收者, 一个 String (当前的 String 值)
                }
    
                val funLit2 = { s: String ->
                    // foo() 的接收者, 因为闭合的 lambda 表达式
                    // 没有接收者
                    val d1 = this
                }
            }
        }
    }
    
    KOTLIN
    fun main() {
        fun printLine() { println("Local function") } // 局部函数
    
        class A {
            fun printLine() { println("Member function") } // 成员函数
    
            fun invokePrintLine(omitThis: Boolean = false) {
                if (omitThis) printLine() // 调用局部函数
                else this.printLine() // 调用成员函数
            }
        }
    
        A().invokePrintLine() // 输出: Member function
        A().invokePrintLine(omitThis = true) // 输出: Local function
    }
    
    KOTLIN
    annotation class Fancy
    
    KOTLIN
    @Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
            AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.VALUE_PARAMETER,
            AnnotationTarget.EXPRESSION) // 可以用于类、函数、类型参数等
    @Retention(AnnotationRetention.SOURCE) // 只保留在源码中,编译后会被丢弃
    @MustBeDocumented // 应该包含在 API 文档中
    annotation class Fancy
    
    KOTLIN
    @Fancy class Foo {
        @Fancy fun baz(@Fancy foo: Int): Int {
            return (@Fancy 1)
        }
    }
    
    KOTLIN
    annotation class Special(val why: String)
    
    @Special("example") class Foo {}
    
    KOTLIN
    annotation class ReplaceWith(val expression: String)
    
    annotation class Deprecated(
        val message: String,
        val replaceWith: ReplaceWith = ReplaceWith("")
    )
    
    @Deprecated("This function is deprecated, use === instead", ReplaceWith("this === other"))
    
    KOTLIN
    import kotlin.reflect.KClass
    
    annotation class Ann(val arg1: KClass<*>, val arg2: KClass<out Any>)
    
    @Ann(String::class, Int::class) class MyClass
    
    KOTLIN
    annotation class InfoMarker(val info: String)
    
    fun processInfo(marker: InfoMarker): Unit = TODO()
    
    fun main(args: Array<String>) {
        if (args.isNotEmpty())
            processInfo(getAnnotationReflective(args))
        else
            processInfo(InfoMarker("default"))
    }
    
    KOTLIN
    annotation class Suspendable
    
    val f = @Suspendable { Fiber.sleep(10) }
    
    KOTLIN
    class Example(@field:Ann val foo,    // 注解 Java 字段
                  @get:Ann val bar,      // 注解 Java getter
                  @param:Ann val quux)   // 注解 Java 构造函数参数
    
    KOTLIN
    class Example {
         @set:[Inject VisibleForTesting]
         var collaborator: Collaborator
    }
    
    JAVA
    // Java
    public @interface Ann {
        int intValue();
        String stringValue();
    }
    // Kotlin
    @Ann(intValue = 1, stringValue = "abc") class C
    
    JAVA
    // Java
    public @interface AnnWithValue {
        String value();
    }
    // Kotlin
    @AnnWithValue("abc") class C
    
    JAVA
    // Java
    public @interface AnnWithArrayValue {
        String[] value();
    }
    // Kotlin
    @AnnWithArrayValue("abc", "foo", "bar") class C
    
    JAVA
    // Java
    public @interface AnnWithArrayMethod {
        String[] names();
    }
    @AnnWithArrayMethod(names = ["abc", "foo", "bar"])
    class C
    
    JAVA
    // Java
    public @interface Ann {
        int value();
    }
    // Kotlin
    fun foo(ann: Ann) {
        val i = ann.value // 直接访问 value 属性
    }
    
    KOTLIN
    @Repeatable
    annotation class Tag(val name: String)
    
    // 编译器会自动生成包含注解 @Tag.Container
    
    KOTLIN
    @JvmRepeatable(Tags::class)
    annotation class Tag(val name: String)
    
    annotation class Tags(val value: Array<Tag>)
    
    KOTLIN
    val (name, age) = person // 将 person 对象的属性赋值给 name 和 age 变量
    println(name) // 可以直接使用 name 变量
    println(age) // 也可以直接使用 age 变量
    
    KOTLIN
    val name = person.component1() // 等价于 name = person 的第一个属性
    val age = person.component2()  // 等价于 age = person 的第二个属性
    
    KOTLIN
    for ((a, b) in collection) {
        // a 和 b 分别是集合中每个元素的 component1() 和 component2() 的返回值
    }
    
    KOTLIN
    data class Result(val result: Int, val status: String)
    
    fun function(): Result {
        // ...
        return Result(123, "Success")
    }
    
    val (result, status) = function() // 直接将函数返回的 Result 对象解构
    println(result) // 输出 123
    println(status) // 输出 Success
    
    KOTLIN
    val map = mapOf("key1" to "value1", "key2" to "value2")
    
    for ((key, value) in map) {
        println("Key: $key, Value: $value")
    }
    
    KOTLIN
    val (_, status) = getResult() // 只关心 status,忽略第一个返回值
    
    KOTLIN
    map.mapValues { (key, value) -> "$value!" } // 直接在 lambda 中解构键值对
    
    map.mapValues { (_, value) -> "$value!" } // 如果不关心 key,可以用 _ 忽略
    
    KOTLIN
    map.mapValues { (_, value): Map.Entry<Int, String> -> "$value!" } // 为整个解构参数指定类型
    map.mapValues { (_, value: String) -> "$value!" } // 为 value 指定类型
    
    KOTLIN
    class MyClass {
        fun myMethod() {}
    }
    
    fun main() {
        val kClass: KClass<MyClass> = MyClass::class
        println(kClass.simpleName) // 输出: MyClass
    
        if (MyClass() is MyClass) {
            println("It's a MyClass instance!")
        }
    
        val javaClass: Class<MyClass> = MyClass::class.java // 获取 Java Class 对象
    }
    
    KOTLIN
    fun isEven(x: Int): Boolean {
        return x % 2 == 0
    }
    
    fun main() {
        val numbers = listOf(1, 2, 3, 4, 5)
    
        val evenNumbers = numbers.filter(::isEven) // 将 isEven 函数作为参数传递
        println(evenNumbers) // 输出: [2, 4]
    
        val functionReference: (Int) -> Boolean = ::isEven // 显式声明类型
    }
    
    KOTLIN
    val myTopLevelProperty = "Hello"
    
    class MyClass(val myMemberProperty: Int)
    
    fun main() {
        println(::myTopLevelProperty.get()) // 输出: Hello
        println(::myTopLevelProperty.name) // 输出: myTopLevelProperty
    
        val myObject = MyClass(123)
        println(MyClass::myMemberProperty.get(myObject)) // 输出: 123
    }
    
    KOTLIN
    class MyClass(val message: String)
    
    fun main() {
        val constructorReference: (String) -> MyClass = ::MyClass
    
        val myInstance = constructorReference("World") // 动态创建 MyClass 实例
        println(myInstance.message) // 输出: World
    }
    
    KOTLIN
    class MyClass {
        fun greet(name: String): String {
            return "Hello, $name!  I am ${this.hashCode()}"
        }
    
        val myProperty = 42
    }
    
    fun main() {
        val myInstance = MyClass()
    
        val boundFunction: (String) -> String = myInstance::greet // 绑定到 myInstance 的 greet 函数
        println(boundFunction("Alice")) // 输出: Hello, Alice! I am ... (myInstance 的 hashCode)
    
        val boundProperty: KProperty0<Int> = myInstance::myProperty // 绑定到 myInstance 的 myProperty 属性
        println(boundProperty.get()) // 输出: 42
    }