ZetCode

Kotlin internal 关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的可见性修饰符控制声明的范围。internal 关键字提供模块级别的可见性。本教程通过实际示例深入探讨了 internal 修饰符。

基本定义

Kotlin 中的 internal 关键字使声明在同一模块内可见。 模块是一组一起编译的 Kotlin 文件。这在大型项目的不同部分之间提供了封装。

Internal 类

Internal 类可以在同一模块内的任何地方访问,但不能在模块外访问。 这对于不应公开的实现细节很有用。

InternalClass.kt
package com.zetcode

internal class DatabaseHelper {
    fun connect() = println("Connecting to database")
}

fun main() {

    val db = DatabaseHelper()
    db.connect() // Works within same module
}

这里我们定义一个 internal 的 DatabaseHelper 类。 它可以在同一模块中使用,但如果导入到另一个模块中,将无法访问。

Internal 函数

函数可以标记为 internal,以将其可见性限制为当前模块。 这有助于隐藏实现细节,同时在整个模块中保持可用性。

InternalFunction.kt
package com.zetcode

internal fun calculateDiscount(price: Double): Double {
    return price * 0.9 // 10% discount
}

fun main() {

    val total = calculateDiscount(100.0)
    println("Discounted price: $total") // Output: 90.0
}

calculateDiscount 函数是 internal 的,因此可以在模块中的任何地方调用它,但不能从其他模块调用。 这保护了业务逻辑。

Internal 属性

属性也可以标记为 internal。 这控制了对类字段的访问,同时使其在整个模块中可用于内部使用。

InternalProperty.kt
package com.zetcode

class ShoppingCart {
    internal var discountApplied = false
    
    fun applyDiscount() {
        discountApplied = true
    }
}

fun main() {

    val cart = ShoppingCart()
    cart.applyDiscount()
    println("Discount applied: ${cart.discountApplied}") // Output: true
}

discountApplied 属性是 internal 的,因此可以在模块内访问,但不能从外部访问。 这保持了封装性。

Internal 接口

接口可以标记为 internal,以将其使用限制为当前模块。 这对于不应在模块外实现的契约很有用。

InternalInterface.kt
package com.zetcode

internal interface Logger {
    fun log(message: String)
}

class ConsoleLogger : Logger {
    override fun log(message: String) {
        println("LOG: $message")
    }
}

fun main() {

    val logger: Logger = ConsoleLogger()
    logger.log("Test message") // Output: LOG: Test message
}

Logger 接口是 internal 的,因此只能在同一模块内实现。 这控制了如何扩展日志记录系统。

在不同包中的 Internal

Internal 可见性在同一模块内的不同包之间工作。 这允许组织,同时保持对 internal 声明的访问。

InternalAcrossPackages.kt
// File 1: com/zetcode/auth/Authenticator.kt
package com.zetcode.auth

internal class Authenticator {
    fun authenticate() = println("Authenticating...")
}

// File 2: com/zetcode/main.kt
package com.zetcode

import com.zetcode.auth.Authenticator

fun main() {

    val auth = Authenticator() // Accessible in same module
    auth.authenticate()
}

Authenticator 类是 internal 的,但可以从同一模块中的另一个包访问。 这演示了模块范围的可见性。

Internal 构造函数

构造函数可以标记为 internal,以控制如何在模块边界之间实例化类,同时允许在模块内构造。

InternalConstructor.kt
package com.zetcode

class ApiClient internal constructor(val apiKey: String) {
    fun makeRequest() = println("Making request with key: $apiKey")
}

fun createClient(key: String): ApiClient {
    return ApiClient(key) // Can call internal constructor
}

fun main() {

    val client = createClient("secret123")
    client.makeRequest() // Output: Making request with key: secret123
}

ApiClient 有一个 internal 构造函数,因此只能在模块内创建。 createClient 函数提供了受控的实例化。

Internal 和继承

Internal 可见性会影响继承。 internal 类或成员可以被同一模块内的子类覆盖,但不能从外部覆盖。

InternalInheritance.kt
package com.zetcode

open internal class BaseService {
    internal open fun start() = println("Base service starting")
}

class CustomService : BaseService() {
    override fun start() {
        println("Custom service starting")
        super.start()
    }
}

fun main() {

    val service = CustomService()
    service.start() 
    // Output: 
    // Custom service starting
    // Base service starting
}

internal 的 BaseService 可以在同一模块内扩展。 start 方法可以被子类中的 internal 或更可见的成员覆盖。

Internal 可见性的最佳实践

来源

Kotlin 可见性修饰符文档

本教程深入探讨了 Kotlin 的 internal 关键字,展示了它如何提供模块级别的可见性控制。 我们探讨了各种场景,包括类、函数、属性和继承。 正确使用 internal 可见性有助于创建封装良好、可维护的模块。

作者

我的名字是 Jan Bodnar,我是一位充满激情的程序员,拥有多年的编程经验。 自 2007 年以来,我一直在撰写编程文章。 到目前为止,我撰写了 1400 多篇文章和 8 本电子书。 我拥有超过八年的编程教学经验。

列出 所有 Kotlin 教程