从 https://kotlinlang.org 翻译过来

Basic syntax overview

Package definition and imports

包声明需置于源文件顶部:

package my.demo

import kotlin.text.*

// ...

Kotlin 不要求目录结构与包名一致:源文件可任意存放于文件系统中。

Program entry point

Kotlin 应用的入口点是 main 函数:

fun main() {
    println("Hello world!")
}

另一种形式的 main 函数支持接收可变长度的字符串参数:

fun main(args: Array<String>) {
    println(args.contentToString())
}

  • print 函数:将参数输出到标准输出(无换行):

    print("Hello ")
    print("world!") // 输出结果:Hello world!
    

  • println 函数:打印参数并添加换行符(后续输出自动换行):

    println("Hello world!")
    println(42) // 输出结果:Hello world!(换行)42(换行)
    

Read from the standard input

readln() 函数用于读取标准输入,接收用户输入的整行内容并返回字符串:

// 打印输入提示信息
println("Enter any word: ")

// 读取用户输入并存储(示例输入:Happiness)
val yourWord = readln()

// 打印包含输入内容的结果
print("You entered the word: ")
print(yourWord) // 输出结果:You entered the word: Happiness

Functions

  1. 带两个 Int 参数且返回 Int 类型的函数:

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

  2. 函数体为表达式(返回类型自动推导):

    fun sum(a: Int, b: Int) = a + b
    

  3. 无有意义返回值的函数(返回 Unit):

    fun printSum(a: Int, b: Int): Unit {
        println("sum of $a and $b is ${a + b}")
    }
    

  4. Unit 返回类型可省略:

    fun printSum(a: Int, b: Int) {
        println("sum of $a and $b is ${a + b}")
    }
    

Variables

Kotlin 中声明变量需以 val 或 var 关键字开头,后跟变量名:

  1. val:声明不可变变量(仅赋值一次,只读,初始化后无法重新赋值):

    // 声明变量 x 并初始化为 5
    val x: Int = 5 // 输出结果:5
    

  2. var:声明可变变量(可在初始化后重新赋值):

    // 声明变量 x 并初始化为 5
    var x: Int = 5
    // 重新赋值为 6
    x += 1 // 输出结果:6
    

  3. 类型推导:Kotlin 支持自动识别变量类型,声明时可省略类型标注:

    // 变量 x 初始化为 5,自动推导为 Int 类型
    val x = 5 // 输出结果:5
    

  4. 延迟初始化:变量必须初始化后才能使用,支持「先声明后初始化」(需显式指定类型):

    // 声明时初始化,无需指定类型
    val x = 5
    // 先声明后初始化,必须指定类型
    val c: Int
    c = 3 // 输出结果:5、3
    

  5. 顶层变量:可在文件顶层声明变量(无需嵌套在类 / 函数中):

    val PI = 3.14
    var x = 0
    
    fun incrementX() {
        x += 1
    }
    // 初始状态:x = 0; PI = 3.14
    // 调用 incrementX() 后:x = 1; PI = 3.14
    

Creating classes and instances

  1. 定义类:使用 class 关键字:

    class Shape
    
  2. 类属性:可在类声明或类体中定义:

    class Rectangle(val height: Double, val length: Double) {
        val perimeter = (height + length) * 2 // 周长(派生属性)
    }
    
  3. 默认构造函数:类声明中列出的参数会自动生成默认构造函数:

    class Rectangle(val height: Double, val length: Double) {
        val perimeter = (height + length) * 2
    }
    
    fun main() {
        val rectangle = Rectangle(5.0, 2.0)
        println("The perimeter is ${rectangle.perimeter}") // 输出:The perimeter is 14.0
    }
    

  4. 类继承:使用冒号(:)声明继承关系。默认情况下类为 final(不可继承),需显式标记 open 才能被继承:

    open class Shape // 可继承的基类
    
    class Rectangle(val height: Double, val length: Double) : Shape() {
        val perimeter = (height + length) * 2
    }

Comments

Kotlin 支持单行注释和多行注释(与大多数现代语言一致):

// 这是单行注释(行尾注释)

/* 这是多行注释
   跨越多行 */

Kotlin 的多行注释支持嵌套

/* 注释开始
/* 包含嵌套注释 */
注释结束 */

String templates

字符串模板

var a = 1
// 模板中引用简单变量名
val s1 = "a is $a" // 结果:"a is 1"

a = 2
// 模板中嵌入任意表达式(使用 ${})
val s2 = "${s1.replace("is", "was")}, but now is $a" // 结果:"a was 1, but now is 2"

if else

条件表达式

  1. 标准条件语句:

    fun maxOf(a: Int, b: Int): Int {
        if (a > b) {
            return a
        } else {
            return b
        }
    }
    

  2. 条件表达式(if 可作为表达式返回值):

    fun maxOf(a: Int, b: Int) = if (a > b) a else b
    

for

  1. 遍历集合元素:

    val items = listOf("apple", "banana", "kiwifruit")
    for (item in items) {
        println(item) // 依次输出:apple、banana、kiwifruit
    }
    

  2. 按索引遍历:

    val items = listOf("apple", "banana", "kiwifruit")
    for (index in items.indices) {
        println("item at $index is ${items[index]}") 
        // 输出:item at 0 is apple、item at 1 is banana、item at 2 is kiwifruit
    }
    

while

val items = listOf("apple", "banana", "kiwifruit")
var index = 0
while (index < items.size) {
    println("item at $index is ${items[index]}")
    index++ // 索引自增
}

when

fun describe(obj: Any): String =
    when (obj) {
        1          -> "One" // 匹配值为 1
        "Hello"    -> "Greeting" // 匹配字符串 "Hello"
        is Long    -> "Long" // 匹配 Long 类型
        !is String -> "Not a string" // 匹配非 String 类型
        else       -> "Unknown" // 默认匹配(无其他匹配时执行)
    }

Ranges

区间

  1. 检查数值是否在区间内(使用 in 运算符):

    val x = 10
    val y = 9
    if (x in 1..y+1) { // 1..10(闭区间,包含首尾)
        println("fits in range") // 输出:fits in range
    }
    

  2. 检查数值是否在区间外(使用 !in 运算符):

    val list = listOf("a", "b", "c")
    
    if (-1 !in 0..list.lastIndex) { // list.lastIndex = 2,区间 0..2
        println("-1 is out of range") // 输出:-1 is out of range
    }
    if (list.size !in list.indices) { // list.indices = 0..2,list.size = 3
        println("list size is out of valid list indices range, too") // 输出该语句
    }
    

  3. 遍历区间:

    for (x in 1..5) {
        print(x) // 输出:12345
    }
    

  4. 遍历步长区间:

    for (x in 1..10 step 2) { // 步长为 2(1、3、5、7、9)
        print(x) // 输出:13579
    }
    println()
    for (x in 9 downTo 0 step 3) { // 倒序遍历,步长为 3(9、6、3、0)
        print(x) // 输出:9630
    }
    

Collections

  1. 遍历集合:

    for (item in items) {
        println(item)
    }
    

  2. 检查集合是否包含元素(使用 in 运算符):

    when {
        "orange" in items -> println("juicy")
        "apple" in items -> println("apple is fine too") // 若 items 包含 "apple",输出该语句
    }
    

  3. 集合过滤与映射(使用 lambda 表达式):

    val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
    fruits
        .filter { it.startsWith("a") } // 过滤以 "a" 开头的元素(avocado、apple)
        .sortedBy { it } // 按字母排序(apple、avocado)
        .map { it.uppercase() } // 转为大写(APPLE、AVOCADO)
        .forEach { println(it) } // 依次输出:APPLE、AVOCADO
    

Nullable values and null checks

可空值与空检查

当变量可能为 null 时,需显式标记为可空类型(类型名后加 ?):

  1. 返回可空值的函数:

    // 若字符串无法转为整数,返回 null
    fun parseInt(str: String): Int? {
        // ...(实现逻辑)
    }
    
  2. 使用返回可空值的函数:

    fun printProduct(arg1: String, arg2: String) {
        val x = parseInt(arg1)
        val y = parseInt(arg2)
    
        // Using `x * y` yields error because they may hold nulls.
        if (x != null && y != null) {
            // x and y are automatically cast to non-nullable after null check
            println(x * y)
        }
        else {
            println("'$arg1' or '$arg2' is not a number")
        }    
    }

        or

// ...
if (x == null) {
    println("Wrong number format in arg1: '$arg1'")
    return
}
if (y == null) {
    println("Wrong number format in arg2: '$arg2'")
    return
}

// x and y are automatically cast to non-nullable after null check
println(x * y)

Type checks and automatic casts

类型检查与自动类型转换

is 运算符用于检查表达式是否为某个类型的实例。若对不可变局部变量或属性进行了特定类型检查,则无需显式进行类型转换(编译器会自动完成):

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // 此分支中 `obj` 会自动转换为 String 类型
        return obj.length
    }

    // 类型检查分支外,`obj` 仍为 Any 类型
    return null
}

另一种写法(提前返回空值):

fun getStringLength(obj: Any): Int? {
    if (obj !is String) return null // 若不是 String 类型,直接返回 null

    // 此分支中 `obj` 自动转换为 String 类型
    return obj.length
}

Keywords and operators

Hard keywords

以下标记始终被解析为关键字,不能用作标识符:

关键字 用途说明
as 用于类型转换;为导入声明指定别名
as? 用于安全类型转换(转换失败返回 null
break 终止循环执行
class 声明类
continue 跳转到最近外层循环的下一次迭代
do 开始 do/while 循环(后置条件循环)
else 定义 if 表达式中条件为 false 时执行的分支
false 指定布尔类型的「假」值
for 开始 for 循环
fun 声明函数
if 开始 if 表达式
in 指定 for 循环中被迭代的对象;作为中缀运算符检查值是否属于某个范围、集合或定义了 contains 方法的实体;在 when 表达式中用于相同目的;标记类型参数为逆变(contravariant)
!in 作为运算符检查值不属于某个范围、集合或定义了 contains 方法的实体;在 when 表达式中用于相同目的
interface 声明接口
is 检查值是否为特定类型;在 when 表达式中用于相同目的
!is 检查值不是特定类型;在 when 表达式中用于相同目的
null 表示不指向任何对象的常量引用
object 同时声明类及其实例(单例 / 伴生对象)
package 指定当前文件所属的包
return 从最近的外层函数或匿名函数返回
super 引用超类的方法 / 属性实现;从次级构造函数调用超类构造函数
this 引用当前接收者(Receiver);从次级构造函数调用同类的其他构造函数
throw 抛出异常
true 指定布尔类型的「真」值
try 开始异常处理块
typealias 声明类型别名
typeof 预留作未来使用
val 声明只读属性或局部变量
var 声明可变属性或局部变量
when 开始 when 表达式(执行给定分支中的一个)
while 开始 while 循环(前置条件循环)

Soft keywords

以下标记仅在适用上下文中作为关键字,在其他上下文可作为标识符:

关键字 用途说明
by 将接口实现委托给另一个对象;将属性访问器的实现委托给另一个对象
catch 开始处理特定异常类型的代码块
constructor 声明主构造函数或次级构造函数
delegate 用作注解的使用位置目标(annotation use-site target)
dynamic 在 Kotlin/JS 代码中引用动态类型
field 用作注解的使用位置目标
file 用作注解的使用位置目标
finally 开始在 try 块退出时始终执行的代码块
get 声明属性的 getter;用作注解的使用位置目标
import 将其他包中的声明导入当前文件
init 开始初始化块
param 用作注解的使用位置目标
property 用作注解的使用位置目标
receiver 用作注解的使用位置目标
set 声明属性的 setter;用作注解的使用位置目标
setparam 用作注解的使用位置目标
value 与 class 关键字搭配声明内联类(inline class)
where 指定泛型类型参数的约束条件

Modifier keywords

以下标记仅在声明的修饰符列表中作为关键字,在其他上下文可作为标识符:

关键字 用途说明
abstract 将类或成员标记为抽象
actual 在多平台项目中表示平台特定的实现
annotation 声明注解类
companion 声明伴生对象
const 将属性标记为编译时常量
crossinline 禁止在传递给内联函数的 lambda 中使用非局部返回
data 指示编译器为类生成规范成员(equals()/hashCode()/toString() 等)
enum 声明枚举类
expect 将声明标记为平台特定,期望在平台模块中提供实现
external 将声明标记为在 Kotlin 外部实现(通过 JNI 或 JavaScript 访问)
final 禁止覆盖成员
infix 允许使用中缀表示法调用函数
inline 告诉编译器在调用点内联函数及其传入的 lambda
inner 允许从嵌套类引用外部类实例
internal 将声明标记为在当前模块内可见
lateinit 允许在构造函数外初始化非空属性
noinline 关闭传递给内联函数的 lambda 的内联
open 允许类被继承或成员被覆盖
operator 将函数标记为重载运算符或实现约定
out 标记类型参数为协变(covariant)
override 将成员标记为覆盖超类成员
private 将声明标记为仅在当前类或文件中可见
protected 将声明标记为仅在当前类及其子类中可见
public 将声明标记为在任何位置可见
reified 将内联函数的类型参数标记为在运行时可访问
sealed 声明密封类(子类受限的类)
suspend 将函数或 lambda 标记为挂起(可用于协程)
tailrec 将函数标记为尾递归(允许编译器用迭代替换递归)
vararg 允许参数接收可变数量的参数

Special identifiers

特殊标识符

以下标识符由编译器在特定上下文中定义,在其他上下文可作为常规标识符:

标识符 用途说明
field 在属性访问器内部引用属性的幕后字段(backing field)
it 在 lambda 内部隐式引用其参数

Operators and special symbols

Kotlin 支持以下运算符和特殊符号:

符号 / 运算符 用途说明
+-*/% 数学运算符;* 也用于将数组传递给 vararg 参数
= 赋值运算符;用于指定参数的默认值
+=-=*=/=%= 增强赋值运算符
++-- 自增 / 自减运算符
&&, ` !` 逻辑「与」「或」「非」运算符(位运算需使用对应的中缀函数替代)
==!= 相等运算符(非基本类型会转换为 equals() 调用)
===!== 引用相等运算符(检查是否指向同一对象)
<><=>= 比较运算符(非基本类型会转换为 compareTo() 调用)
[] 索引访问运算符(转换为 get 和 set 调用)
!! 断言表达式非空(若为空则抛出 NullPointerException
?. 安全调用(接收者非空时调用方法 / 访问属性,为空则返回 null
?: Elvis 运算符(左值为 null 时取右值)
:: 创建成员引用或类引用
....< 创建范围(.. 为闭区间,..< 为半开区间)
: 在声明中分隔名称与类型
? 标记类型为可空
-> 分隔 lambda 表达式的参数与体;分隔函数类型中的参数与返回类型声明;分隔 when 表达式分支的条件与体
@ 引入注解;引入或引用循环标签;引入或引用 lambda 标签;引用外部作用域的 this 表达式;引用外部超类
; 分隔同一行的多个语句
$ 在字符串模板中引用变量或表达式
_ 替代 lambda 表达式中未使用的参数;替代解构声明中未使用的参数

Packages and imports

Packages and imports

源文件可以以包声明开头:

package org.example

fun printMessage() { /*...*/ }
class Message { /*...*/ }

// ...

源文件中的所有内容(例如类和函数)均包含在该包中。因此,在上述示例中:

  • 函数 printMessage() 的全名为 org.example.printMessage
  • 类 Message 的全名为 org.example.Message

若未指定包声明,则该文件的所有内容属于无名称的默认包

Default imports

Kotlin 会为每个文件 Default imports 多个包,无需手动声明:

  • kotlin.*
  • kotlin.annotation.*
  • kotlin.collections.*
  • kotlin.comparisons.*
  • kotlin.io.*
  • kotlin.ranges.*
  • kotlin.sequences.*
  • kotlin.text.*

平台特定的额外导入

根据目标平台不同,会额外导入以下包:

JVM 平台:

  • java.lang.*
  • kotlin.jvm.*

JS 平台:

  • kotlin.js.*

Imports

除默认导入外,每个文件还可包含自定义的导入指令

1. 导入单个名称

import org.example.Message // 无需限定名即可直接使用 Message

2. 导入某个作用域的所有可访问内容

(支持包、类、对象等作用域)

import org.example.* // 'org.example' 包下的所有内容均可直接使用

3. 名称冲突解决

若导入的内容存在名称冲突,可使用 as 关键字局部重命名冲突实体:

import org.example.Message // 直接使用 Message(对应 org.example.Message)
import org.test.Message as TestMessage // 用 TestMessage 指代 org.test.Message

4. 导入范围扩展

import 关键字并非仅用于导入类,还支持导入其他声明:

  • 顶层函数与属性
  • 对象声明中定义的函数与属性
  • 枚举常量

顶层声明的可见性

若顶层声明被标记为 private,则其仅在声明所在的文件内可见(详见「可见性修饰符」)。

Annotations

注解是向代码附加元数据的一种方式。声明注解时,需在类前添加 annotation 修饰符:

annotation class Fancy

注解的额外属性可通过元注解(标注在注解类上的注解)指定:

  • @Target:指定该注解可标注的元素类型(如类、函数、属性、表达式等);
  • @Retention:指定注解是否存储在编译后的类文件中,以及是否可在运行时通过反射访问(默认均为 true);
  • @Repeatable:允许在单个元素上多次使用同一注解;
  • @MustBeDocumented:指定该注解属于公共 API,需包含在生成的 API 文档的类或方法签名中。
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
        AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.VALUE_PARAMETER,
        AnnotationTarget.EXPRESSION)
@Retention(AnnotationRetention.SOURCE)
@MustBeDocumented
annotation class Fancy

Usage

@Fancy class Foo {
    @Fancy fun baz(@Fancy foo: Int): Int {
        return (@Fancy 1) // 标注表达式
    }
}
// 1. 标注在类上:整个 Foo 类被 @Fancy 注解标记
@Fancy class Foo {
    // 2. 标注在函数上:baz() 函数被 @Fancy 注解标记
    @Fancy fun baz(
        // 3. 标注在函数参数上:foo 参数被 @Fancy 注解标记
        @Fancy foo: Int
    ): Int {
        // 4. 标注在表达式上:数值 1 这个表达式被 @Fancy 注解标记
        return (@Fancy 1) // 标注表达式
    }
}

如果把 @Fancy 换成实际有功能的注解(比如 @Deprecated 或自定义的日志注解),就更容易理解:

@Deprecated("Foo 类已过时") // 标注类
class Foo {
    @Deprecated("baz 函数已过时") // 标注函数
    fun baz(
        @NotNull foo: Int // 标注参数(非空校验)
    ): Int {
        return (@CheckReturnValue 1) // 标注返回值表达式(校验返回值是否被使用)
    }
}

若需标注类的主构造函数,需显式添加 constructor 关键字,并在其前添加注解:

class Foo @Inject constructor(dependency: MyDependency) { ... }

也可标注属性的访问器(getter/setter):

class Foo {
    var x: MyDependency? = null
        @Inject set // 标注 setter 方法
}
构造函数(Constructors)

注解可包含带参数的构造函数:

annotation class Special(val why: String)

@Special("example") class Foo {}

允许的参数类型

  1. 与 Java 基本类型对应的类型(IntLong 等);
  2. 字符串(String);
  3. 类(Foo::class);
  4. 枚举(enum);
  5. 其他注解;
  6. 上述类型的数组。

注意:注解参数不能是可空类型,因为 JVM 不支持将 null 存储为注解属性值。

若注解作为其他注解的参数,其名称无需添加 @ 前缀

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"))
fun oldFunction(other: Any): Boolean = this == other

若需将类指定为注解参数,需使用 Kotlin 类(KClass)——Kotlin 编译器会自动将其转换为 Java 类,确保 Java 代码可正常访问该注解及参数:

import kotlin.reflect.KClass

annotation class Ann(val arg1: KClass<*>, val arg2: KClass<out Any>)

@Ann(String::class, Int::class) class MyClass

实例化(Instantiation)

在 Java 中,注解类型是接口的一种形式,可实现并创建实例;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")) // 直接实例化注解
}

Lambda 表达式(Lambdas)

注解也可用于 Lambda 表达式,此时注解会应用于 Lambda 体生成的 invoke() 方法。该特性适用于 Quasar 等框架(通过注解实现并发控制):

annotation class Suspendable

val f = @Suspendable { Fiber.sleep(10) } // 注解 Lambda 表达式

注解使用位置目标(Annotation use-site targets)

当标注属性或主构造函数参数时,对应的 Kotlin 元素会生成多个 Java 元素(如字段、getter、构造函数参数),因此注解在生成的 Java 字节码中有多个可能的位置。需通过特定语法指定注解的生成位置:

class Example(
    @field:Ann val foo,    // 仅标注 Java 字段
    @get:Ann val bar,      // 仅标注 Java getter 方法
    @param:Ann val quux    // 仅标注 Java 构造函数参数
)

该语法也可用于标注整个文件:需将带 file 目标的注解放在文件顶层(包声明之前,或默认包下的所有导入之前):

@file:JvmName("Foo") // 标注文件,指定生成的 Java 类名为 "Foo"

package org.jetbrains.demo

若多个注解有相同的使用位置目标,可将目标后加方括号,所有注解放入括号中(all 元目标除外),避免重复目标声明:

class Example {
     @set:[Inject VisibleForTesting] // 同时标注 setter 方法
     var collaborator: Collaborator
}

支持的使用位置目标完整列表

目标名称 说明
file 标注整个文件
field 标注 Java 字段
property 标注 Kotlin 属性(该目标的注解对 Java 不可见)
get 标注属性的 getter 方法
set 标注属性的 setter 方法
all 实验性元目标(用于属性,详见下文说明)
receiver 标注扩展函数 / 属性的接收者参数
param 标注构造函数参数
setparam 标注属性 setter 的参数
delegate 标注委托属性中存储委托实例的字段

标注扩展函数的接收者参数:

fun @receiver:Fancy String.myExtension() { ... } // 标注 String 类型的接收者

未指定使用位置目标时的默认行为

若未指定使用位置目标,目标会根据被使用注解的 @Target 自动选择。若有多个适用目标,按以下顺序选择第一个适用目标:

  1. param(构造函数参数)
  2. property(Kotlin 属性)
  3. field(Java 字段)

以 Jakarta Bean Validation 的 @Email 注解为例:

// Java 中的 @Email 注解
@Target(value={METHOD,FIELD,ANNOTATION_TYPE,CONSTRUCTOR,PARAMETER,TYPE_USE})
public @interface Email { }

应用于 Kotlin 代码:

data class User(
    val username: String,
    @Email val email: String, // 等价于 @param:Email(未指定目标时,优先选择 param)
    @Email val secondaryEmail: String? = null // 等价于 @field:Email(属性字段,选择 field)
)

Kotlin 2.2.0 引入了实验性默认规则,使注解向参数、字段、属性的传播更可预测。新规则下,若有多个适用目标,选择逻辑如下:

  1. 若 param(构造函数参数)适用,则使用该目标;
  2. 若 property(Kotlin 属性)适用,则使用该目标;
  3. 若 field(Java 字段)适用且 property 不适用,则使用 field

沿用上述示例,新规则下的行为:

data class User(
    val username: String,
    @Email val email: String, // 等价于 @param:Email + @field:Email(同时标注两个目标)
    @Email val secondaryEmail: String? = null // 仍等价于 @field:Email
)

若多个目标均不包含 parampropertyfield,则注解无效。

启用新默认规则:需在 Gradle 配置中添加以下编译参数:

// build.gradle.kts
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xannotation-default-target=param-property")
    }
}

恢复旧行为的方式:

  1. 特定场景:显式指定目标(如用 @param:Annotation 替代 @Annotation);
  2. 整个项目:在 Gradle 中添加以下编译参数:
// build.gradle.kts
kotlin {
    compilerOptions {
        freeCompilerArgs.add("-Xannotation-default-target=first-only")
    }
}

说明:上述新默认规则为实验性特性,未来可能调整。

Visibility modifiers

可见性修饰符

类、对象、接口、构造函数、函数,以及属性及其 setter 方法均可拥有可见性修饰符。Getter 方法的可见性始终与其所属属性相同。

Kotlin 中有四种可见性修饰符:privateprotectedinternal 和 public。默认可见性为 public

Packages

函数、属性、类、对象和接口可直接声明在包内的 “顶层”:

// 文件名称:example.kt
package foo

fun baz() { ... }
class Bar { ... }
  • 若未使用可见性修饰符,默认使用 public,表示声明在任何地方均可见。
  • 若标记为 private,则仅在包含该声明的文件内可见。
  • 若标记为 internal,则在同一模块内的任何地方可见。//即一个gradle文件的范围
  • protected 修饰符不适用于顶层声明

要从其他包使用可见的顶层声明,需导入该声明。

// 文件名称:example.kt
package foo

private fun foo() { ... } // 仅在 example.kt 内可见

public var bar: Int = 5 // 属性在任何地方可见
    private set         // setter 仅在 example.kt 内可见

internal val baz = 6    // 同一模块内可见

Class members

对于类内部声明的成员:

  • private:成员仅在当前类内部可见(包括其所有成员)。
  • protected:成员拥有与 private 相同的可见性,且在子类中也可见。
  • internal:同一模块内任何能看到声明类的客户端,均可看到其 internal 成员。
  • public:任何能看到声明类的客户端,均可看到其 public 成员。

注意:在 Kotlin 中,外部类无法访问其内部类的 private 成员。

若重写 protected 或 internal 成员且未显式指定可见性,重写后的成员将继承原成员的可见性。

示例

kotlin

open class Outer {
    private val a = 1
    protected open val b = 2
    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 也不可见
}

构造函数(Constructors)

使用以下语法指定类主构造函数的可见性:

需显式添加 constructor 关键字。

kotlin

class C private constructor(a: Int) { ... }

此处构造函数为 private。默认情况下,所有构造函数均为 public,即类可见的地方构造函数均可见(这意味着 internal 类的构造函数仅在同一模块内可见)。

对于密封类(sealed class),构造函数默认是 protected。更多信息参见「密封类(Sealed classes)」。


局部声明(Local declarations)

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


模块(Modules)

internal 可见性修饰符表示成员在同一模块内可见。具体来说,模块是一组一起编译的 Kotlin 文件,例如:

  • IntelliJ IDEA 模块。
  • Maven 项目。
  • Gradle 源集(例外:test 源集可访问 main 源集的 internal 声明)。
  • 通过一次 <kotlinc> Ant 任务编译的文件集合。

总结

  • Kotlin 的可见性修饰符分为 private(文件 / 类内)、protected(类 + 子类)、internal(模块内)、public(全局),默认是 public
  • 顶层声明不支持 protected,局部声明无可见性修饰符。
  • internal 是 Kotlin 特有修饰符,以 “模块” 为边界控制可见性,适合多模块项目协作(如车载开发中的模块化架构)。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐