Kotlin internal 关键字
最后修改于 2025 年 4 月 19 日
Kotlin 的可见性修饰符控制声明的范围。internal 关键字提供模块级别的可见性。本教程通过实际示例深入探讨了 internal 修饰符。
基本定义
Kotlin 中的 internal 关键字使声明在同一模块内可见。 模块是一组一起编译的 Kotlin 文件。这在大型项目的不同部分之间提供了封装。
Internal 类
Internal 类可以在同一模块内的任何地方访问,但不能在模块外访问。 这对于不应公开的实现细节很有用。
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,以将其可见性限制为当前模块。 这有助于隐藏实现细节,同时在整个模块中保持可用性。
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。 这控制了对类字段的访问,同时使其在整个模块中可用于内部使用。
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,以将其使用限制为当前模块。 这对于不应在模块外实现的契约很有用。
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 声明的访问。
// 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,以控制如何在模块边界之间实例化类,同时允许在模块内构造。
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 类或成员可以被同一模块内的子类覆盖,但不能从外部覆盖。
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 可见性的最佳实践
- 用于模块 API:将实现细节标记为 internal,同时公开公共 API。
- 组织大型项目:使用 internal 在模块内共享代码,同时将其隐藏在其他模块中。
- 与其他修饰符结合使用:Internal 可以与 protected 或 private 结合使用,以实现更精确的控制。
- 记录 internal API: 清楚地记录 internal 声明的预期用法,供模块维护人员参考。
- 考虑替代可见性:对于真正私有的代码,使用 private 而不是 internal。
来源
本教程深入探讨了 Kotlin 的 internal 关键字,展示了它如何提供模块级别的可见性控制。 我们探讨了各种场景,包括类、函数、属性和继承。 正确使用 internal 可见性有助于创建封装良好、可维护的模块。
作者
列出 所有 Kotlin 教程。