包、导入与可见性¶
包、导入和可见性共同决定一段 Kotlin 代码的命名空间和 API 边界。对 Java 开发者来说,Kotlin 的包和导入整体很熟悉,但顶层声明、文件级 private、internal 这些特性会改变代码组织方式。
包声明¶
Kotlin 文件可以从包声明开始:
package org.example.user
fun printUserName(name: String) {
println(name)
}
class User(val name: String)
这个文件中的顶层函数和类都属于 org.example.user 包:
org.example.user.printUserNameorg.example.user.User
如果不写包声明,文件内容属于默认包。实际项目中不建议使用默认包,因为它会让大型项目的命名和导入变得混乱。
包名与目录¶
Kotlin 不强制包名和目录结构完全一致。也就是说,下面这种情况在语言层面可以成立:
src/main/kotlin/foo/User.kt
package com.example.user
但工程实践中仍建议保持一致,因为 IDE、构建工具、代码搜索和团队习惯都会假设二者一致。
默认导入¶
每个 Kotlin 文件都会默认导入一些常用包,例如:
kotlin.*kotlin.collections.*kotlin.io.*kotlin.ranges.*kotlin.sequences.*kotlin.text.*
JVM 平台还会默认导入:
java.lang.*kotlin.jvm.*
所以你可以直接使用 String、Int、List、println 等常见类型和函数。
普通导入与星号导入¶
导入单个声明:
import org.example.user.User
导入某个作用域下的所有可访问声明:
import org.example.user.*
Kotlin 的 import 不只用于类,也可以导入:
- 顶层函数。
- 顶层属性。
object中的函数和属性。- 枚举常量。
示例:
import kotlin.math.max
fun main() {
println(max(10, 20))
}
Java 中静态方法通常需要 static import;Kotlin 没有 static import 关键字,因为顶层函数和对象成员可以直接通过普通 import 导入。
导入别名¶
命名冲突时使用 as:
import org.example.Message
import org.test.Message as TestMessage
val a = Message()
val b = TestMessage()
这在同时使用多个库、多个模块存在同名类时很有用。
顶层声明的可见性¶
Kotlin 允许顶层函数、属性和类:
private fun normalizeName(name: String): String =
name.trim()
顶层 private 的作用域是“当前文件”,不是当前包。这个特性很适合把文件内部辅助函数隐藏起来,不需要创建私有工具类。
可见性修饰符总览¶
Kotlin 常见可见性修饰符:
| 修饰符 | 顶层声明 | 类成员 |
|---|---|---|
public |
任意地方可见,默认值 | 任意地方可见,默认值 |
private |
当前文件可见 | 当前类内部可见 |
protected |
不可用于顶层声明 | 当前类和子类可见 |
internal |
当前模块可见 | 当前模块可见 |
public 是默认值,通常不需要显式写出。
internal¶
internal 表示当前模块可见:
internal class TokenParser
“模块”通常由构建系统决定,例如一个 Gradle source set、一个 Maven module 或一次编译单元。它不是 Java 的 package-private。
Java 对比:
- Java 的默认可见性是包内可见。
- Kotlin 没有 Java 那种 package-private。
- Kotlin 的
internal是模块级边界,更适合多模块项目隐藏实现。
类成员可见性¶
open class Base {
public val a = 1
private val b = 2
protected val c = 3
internal val d = 4
}
注意:
private成员只在当前类内部可见。protected成员在子类可见,但不像 Java 那样还对同包可见。internal成员在当前模块可见。
构造函数可见性¶
class User private constructor(val name: String) {
companion object {
fun create(name: String): User {
require(name.isNotBlank())
return User(name)
}
}
}
私有构造函数常用于工厂方法、单例控制或限制实例创建。
实践建议¶
- 文件内部辅助函数优先使用顶层
private,不必创建Utils类。 - 模块内部实现类使用
internal,公共 API 保持小而清晰。 - 不要滥用星号导入;团队应交给 IDE 格式化规则统一处理。
- Java 迁移时不要把 package-private 机械翻译成
internal,先确认边界是“包”还是“模块”。 protected在 Kotlin 中比 Java 更严格,不提供同包访问能力。
参考¶
- 官方包与导入:https://kotlinlang.org/docs/packages.html
- 官方可见性修饰符:https://kotlinlang.org/docs/visibility-modifiers.html