kotlin基础语法
从 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 to the standard output
-
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
-
带两个
Int参数且返回Int类型的函数:fun sum(a: Int, b: Int): Int { return a + b } -
函数体为表达式(返回类型自动推导):
fun sum(a: Int, b: Int) = a + b -
无有意义返回值的函数(返回
Unit):fun printSum(a: Int, b: Int): Unit { println("sum of $a and $b is ${a + b}") } -
Unit返回类型可省略:fun printSum(a: Int, b: Int) { println("sum of $a and $b is ${a + b}") }
Variables
Kotlin 中声明变量需以 val 或 var 关键字开头,后跟变量名:
-
val:声明不可变变量(仅赋值一次,只读,初始化后无法重新赋值):// 声明变量 x 并初始化为 5 val x: Int = 5 // 输出结果:5 -
var:声明可变变量(可在初始化后重新赋值):// 声明变量 x 并初始化为 5 var x: Int = 5 // 重新赋值为 6 x += 1 // 输出结果:6 -
类型推导:Kotlin 支持自动识别变量类型,声明时可省略类型标注:
// 变量 x 初始化为 5,自动推导为 Int 类型 val x = 5 // 输出结果:5 -
延迟初始化:变量必须初始化后才能使用,支持「先声明后初始化」(需显式指定类型):
// 声明时初始化,无需指定类型 val x = 5 // 先声明后初始化,必须指定类型 val c: Int c = 3 // 输出结果:5、3 -
顶层变量:可在文件顶层声明变量(无需嵌套在类 / 函数中):
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
-
定义类:使用
class关键字:class Shape -
类属性:可在类声明或类体中定义:
class Rectangle(val height: Double, val length: Double) { val perimeter = (height + length) * 2 // 周长(派生属性) } -
默认构造函数:类声明中列出的参数会自动生成默认构造函数:
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 } -
类继承:使用冒号(
:)声明继承关系。默认情况下类为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
条件表达式
-
标准条件语句:
fun maxOf(a: Int, b: Int): Int { if (a > b) { return a } else { return b } } -
条件表达式(
if可作为表达式返回值):fun maxOf(a: Int, b: Int) = if (a > b) a else b
for
-
遍历集合元素:
val items = listOf("apple", "banana", "kiwifruit") for (item in items) { println(item) // 依次输出:apple、banana、kiwifruit } -
按索引遍历:
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
区间
-
检查数值是否在区间内(使用
in运算符):val x = 10 val y = 9 if (x in 1..y+1) { // 1..10(闭区间,包含首尾) println("fits in range") // 输出:fits in range } -
检查数值是否在区间外(使用
!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") // 输出该语句 } -
遍历区间:
for (x in 1..5) { print(x) // 输出:12345 } -
遍历步长区间:
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
-
遍历集合:
for (item in items) { println(item) } -
检查集合是否包含元素(使用
in运算符):when { "orange" in items -> println("juicy") "apple" in items -> println("apple is fine too") // 若 items 包含 "apple",输出该语句 } -
集合过滤与映射(使用 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 时,需显式标记为可空类型(类型名后加 ?):
-
返回可空值的函数:
// 若字符串无法转为整数,返回 null fun parseInt(str: String): Int? { // ...(实现逻辑) } -
使用返回可空值的函数:
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 {}
允许的参数类型:
- 与 Java 基本类型对应的类型(
Int、Long等); - 字符串(
String); - 类(
Foo::class); - 枚举(
enum); - 其他注解;
- 上述类型的数组。
注意:注解参数不能是可空类型,因为 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 自动选择。若有多个适用目标,按以下顺序选择第一个适用目标:
param(构造函数参数)property(Kotlin 属性)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 引入了实验性默认规则,使注解向参数、字段、属性的传播更可预测。新规则下,若有多个适用目标,选择逻辑如下:
- 若
param(构造函数参数)适用,则使用该目标; - 若
property(Kotlin 属性)适用,则使用该目标; - 若
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
)
若多个目标均不包含 param、property、field,则注解无效。
启用新默认规则:需在 Gradle 配置中添加以下编译参数:
// build.gradle.kts
kotlin {
compilerOptions {
freeCompilerArgs.add("-Xannotation-default-target=param-property")
}
}
恢复旧行为的方式:
- 特定场景:显式指定目标(如用
@param:Annotation替代@Annotation); - 整个项目:在 Gradle 中添加以下编译参数:
// build.gradle.kts
kotlin {
compilerOptions {
freeCompilerArgs.add("-Xannotation-default-target=first-only")
}
}
说明:上述新默认规则为实验性特性,未来可能调整。
Visibility modifiers
可见性修饰符
类、对象、接口、构造函数、函数,以及属性及其 setter 方法均可拥有可见性修饰符。Getter 方法的可见性始终与其所属属性相同。
Kotlin 中有四种可见性修饰符:private、protected、internal 和 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 特有修饰符,以 “模块” 为边界控制可见性,适合多模块项目协作(如车载开发中的模块化架构)。
更多推荐


所有评论(0)