Kotlin 的可见性修饰符(Visiblity Modifiers)与 Java 有显著不同,而且设计上更符合现代编程习惯。
下面我为你彻底梳理 Kotlin 的可见性规则,并附上与 Java 的对比。
1. 四大修饰符速览表(最核心)
| 修饰符 | 顶层声明 (类/函数/属性) | 类内部成员 (属性/方法/内部类) | 说明 |
|---|---|---|---|
public |
到处可见(默认) | 到处可见(默认) | 不写修饰符时默认就是这个。 |
private |
仅当前文件内可见 | 仅当前类内部可见 | 比 Java 更严格(Java 的 private 是类级,Kotlin 顶层是文件级)。 |
protected |
(不允许使用) | 当前类 + 子类可见 | 注意:不同包下的子类可见,但不同包下的非子类不可见。 |
internal |
整个 Module 模块内可见 | 整个 Module 模块内可见 | 这是 Kotlin 独有的,Java 没有。 |
2. 关键细节深入解读
① internal 是什么?(最重要区别)
internal 是 Kotlin 相比 Java 最大的进步。它解决了一个痛点:Java 的 package-private(默认修饰符)只能控制同一个包下可见,但包结构容易被绕过。
-
作用域:
internal限制在同一个编译模块(Module)内。 - 模块定义:一个模块就是一个一起编译的工程,比如一个 Gradle 子项目、一个 Maven 模块,或者一个 Android Library。
-
实际好处:如果你写了一个 SDK 库,想暴露给库内部所有包使用,但不暴露给外部 App 开发者,就用
internal。外部 App 完全无法访问它。
② protected 的特殊限制
- 在 Java 中,
protected允许同包下的其他类访问(哪怕不是子类)。 - 在 Kotlin 中,
protected只允许子类访问,不允许同包下的非子类访问。这是为了更纯粹地遵循“保护继承”的语义。
③ 顶层声明中的 private
- 在 Java 中,文件级
private是不存在的(只有类级)。 - 在 Kotlin 中,如果你在一个
.kt文件里写private fun helper(),那么只有这个文件里的代码能调用它,别的文件哪怕在同一个包下也看不见。
3. 与 Java 的对比(面试/迁移高频点)
| 场景 | Java | Kotlin |
|---|---|---|
| 默认修饰符 |
default(包级私有) |
public(公开) |
| 包级私有 | 支持(默认) |
不支持(改用 internal 代替) |
| 文件级私有 | 不支持 | 支持(private 顶层声明) |
| 内部类访问外部 | 内部类默认持有外部类引用,可访问私有成员 | 内部类(Inner Class)不能直接访问外部类的 private 成员(需加 this@Outer 或改为 protected) |
| 同一文件多个类 |
default 可见 |
若无修饰符,public 完全可见 |
4. 实战代码示例
// ---- 文件1: Demo.kt ----
package com.example.demo
// 1. 顶层声明 (默认 public)
val version = "1.0" // 全局可见
// 2. 顶层 private (仅当前文件可见)
private fun log(msg: String) {
println(msg)
}
// 3. internal 类 (仅当前 Module 可见)
internal class InternalHelper {
fun help() = "Helping"
}
// 4. public 类 (默认)
class User {
// 5. private 属性 (仅类内部)
private val id = 1
// 6. protected 属性 (类 + 子类)
protected val name = "Tom"
// 7. internal 方法 (Module 内可见)
internal fun printId() {
println(id)
log("打印 ID") // 可以调用文件顶层 private 函数
}
}
// 8. 子类访问 protected
class Admin : User() {
fun show() {
println(name) // ✅ 可以访问父类的 protected
// println(id) // ❌ 不可以,id 是 private
}
}
5. 最佳实践建议(避坑指南)
-
默认就用
public:Kotlin 的设计哲学就是“默认公开”,因为隐藏细节应该是有意识的行为。 -
库开发多用
internal:不要用public暴露你不想让外部调用的 API,否则用户会在 IDE 提示里看到一堆杂音。用internal既能共享代码,又能保持 SDK 接口干净。 -
慎用
protected在顶层:记住顶层声明(文件级)写protected是编译报错的,因为文件没有“子类”概念。 -
Java 调用 Kotlin 的注意:
internal在 Java 眼中会变成public(因为 JVM 没有模块概念),所以编译后 Kotlin 会进行名字修饰(Name Mangling),Java 代码虽然能看见,但调用起来很别扭(需要加后缀),这实际上阻止了 Java 直接调用。