Kotlin 2.1.0 入门教程(十九)可见性

146 阅读4分钟

可见性修饰符

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

有四种可见性修饰符:privateprotectedinternalpublic。默认的可见性是 public

在本文章中,你将了解这些修饰符如何应用于不同类型的声明作用域。

函数、属性、类、对象和接口可以在包内的顶级位置直接声明:

// 文件名:example.kt
package foo

fun baz() { ... }
class Bar { ... }
  • 如果你不使用可见性修饰符,默认会使用 public,这意味着你的声明在任何地方都可见。

  • 如果你将一个声明标记为 private,那么它仅在包含该声明的文件内部可见。

  • 如果你将其标记为 internal,那么它在同一个模块内的任何地方都可见。

  • protected 修饰符不能用于顶级声明。

要从另一个包中使用可见的顶级声明,你需要导入它。

// 文件名:example.kt
package foo

private fun foo() { ... } // 该函数仅在 example.kt 文件内部可见。

public var bar: Int = 5 // 该属性在任何地方都可见。
    private set         // 该属性的 setter 仅在 example.kt 文件内部可见。

internal val baz = 6 // 该属性在同一个模块内可见。

类成员

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

  • private 表示该成员仅在这个类内部可见(包括该类的所有成员)。

  • protected 表示该成员的可见性与标记为 private 的成员相同,但在子类中也可见。

  • internal 表示在同一个模块内,任何能看到声明该成员的类的客户端,也能看到其 internal 成员。

  • public 表示任何能看到声明该成员的类的客户端,都能看到其 public 成员。

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

如果你重写了一个 protectedinternal 的成员,并且没有显式指定可见性,那么重写后的成员将与原成员具有相同的可见性。

open class Outer {
    // 私有属性 a,仅在 Outer 类内部可见。
    private val a = 1
    // 受保护的开放属性 b,在 Outer 类内部和其子类中可见,且可被子类重写。
    protected open val b = 2
    // 内部开放属性 c,在同一模块内,能看到 Outer 类的地方都可见,且可被子类重写。
    internal open val c = 3
    // 属性 d,默认是 public,在任何能看到 Outer 类的地方都可见。
    val d = 4

    // 受保护的嵌套类 Nested,在 Outer 类内部和其子类中可见。
    protected class Nested {
        // 公共属性 e,在能看到 Nested 类的地方都可见。
        public val e: Int = 5
    }
}

class Subclass : Outer() {
    // 属性 a 不可见,因为它是 Outer 类的私有属性。
    // 属性 b、c 和 d 可见,b 受保护,c 是内部属性,d 是公共属性。
    // 嵌套类 Nested 和其属性 e 可见,因为 Nested 是受保护的,子类可访问。

    // 重写属性 b,重写后的 b 仍然是受保护的。
    override val b = 5   
    // 重写属性 c,重写后的 c 仍然是内部属性。
    override val c = 7   
}

// 定义一个不相关的类 Unrelated,其构造函数接收一个 Outer 类的实例。
class Unrelated(o: Outer) {
    // o.a 和 o.b 不可见,a 是私有属性,b 是受保护属性。
    // o.c 和 o.d 可见(在同一模块内),c 是内部属性,d 是公共属性。
    // Outer.Nested 不可见,Nested 类的属性 e 也不可见,因为 Nested 是受保护的。
}

构造函数

可以使用以下语法来指定类主构造函数的可见性,此时需要显式添加 constructor 关键字。

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

在上述代码中,构造函数是 private。默认情况下,所有构造函数都是 public,这实际上意味着只要类是可见的,其构造函数在任何地方都能被访问(也就是说,internal 类的构造函数仅在同一模块内可见)。

对于密封类,其构造函数默认是 protected 修饰的。

局部声明

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

模块

internal 可见性修饰符表示该成员在同一个模块内可见。

更具体地说,一个模块是一组一起编译的 Kotlin 文件,例如:

  • 一个 IntelliJ IDEA 模块。

  • 一个 Maven 项目。

  • 一个 Gradle 源集(不过 test 源集可以访问 main 源集的 internal 声明)。

  • 通过一次 <kotlinc> Ant 任务调用编译的一组文件。