Kotlin protected 关键字
最后修改于 2025 年 4 月 19 日
Kotlin 的可见性修饰符控制对类成员的访问。protected
关键字将可见性限制为该类及其子类。本教程通过实际例子深入探讨 protected
修饰符。
基本定义
Kotlin 中的 protected
修饰符使成员在其类和子类中可见。与 Java 不同,Kotlin 的 protected 成员在同一包中不可见。Protected 适用于类成员,而不是顶级声明。
基本 protected 属性
一个 protected 属性只能在其声明类和任何子类中访问。这提供了封装,同时允许继承。
package com.zetcode open class Vehicle { protected val maxSpeed = 100 fun showMaxSpeed() { println("Max speed: $maxSpeed") // Accessible here } } class Car : Vehicle() { fun displaySpeed() { println("Car max speed: $maxSpeed") // Accessible in subclass } } fun main() { val car = Car() car.showMaxSpeed() // Output: Max speed: 100 car.displaySpeed() // Output: Car max speed: 100 // println(car.maxSpeed) // Error: Cannot access 'maxSpeed' }
这里 maxSpeed
是 protected 的,因此它可以在 Vehicle 和 Car 中访问,但不能从 main() 访问。如果取消注释,注释行将导致编译错误。
protected 方法
方法也可以标记为 protected,将其使用限制为类层次结构。这对于子类可能需要的内部实现细节很有用。
package com.zetcode open class Animal { protected fun makeSound() { println("Animal sound") } fun publicSound() { makeSound() // Accessible here } } class Dog : Animal() { fun bark() { makeSound() // Accessible in subclass println("Woof!") } } fun main() { val dog = Dog() dog.publicSound() // Output: Animal sound dog.bark() // Output: Animal sound\nWoof! // dog.makeSound() // Error: Cannot access 'makeSound' }
protected 的 makeSound
方法在 Animal 和 Dog 中可访问,但不能从 main() 访问。公共方法可以在需要时公开 protected 功能。
protected 构造函数
protected 构造函数只能由类本身或其子类调用。这对于工厂模式或限制实例化很有用。
package com.zetcode open class Parent protected constructor(val name: String) { companion object { fun create(name: String): Parent { return Parent(name) // Accessible in companion } } } class Child(name: String) : Parent(name) { // Can call protected constructor } fun main() { val parent = Parent.create("John") // Using factory method val child = Child("Alice") println(parent.name) // Output: John println(child.name) // Output: Alice // val direct = Parent("Bob") // Error: Cannot access '' }
Parent 类只能通过其工厂方法或子类实例化。这控制了实例的创建方式,同时允许继承。
protected 和 Internal 冲突
当一个成员既是 protected 又是 internal 时,它对子类和模块成员可见。这展示了修饰符如何在 Kotlin 中组合。
package com.zetcode open class Base { protected internal val secret = "Confidential" } class Derived : Base() { fun reveal() { println(secret) // Accessible in subclass } } fun main() { val derived = Derived() derived.reveal() // Output: Confidential val base = Base() println(base.secret) // Also accessible in same module }
secret
属性在子类和同一模块中都可访问。internal 修饰符将 protected 可见性扩展到模块内。
protected 在接口中
默认情况下,Kotlin 接口不能有 protected 成员。所有接口成员都是公共的。此示例展示了使用抽象类的解决方法。
package com.zetcode interface Vehicle { fun start() // Implicitly public } abstract class ProtectedVehicle : Vehicle { protected abstract val maxSpeed: Int protected open fun internalStart() { println("Starting vehicle") } override fun start() { internalStart() } } class Car : ProtectedVehicle() { override val maxSpeed = 120 override fun internalStart() { println("Starting car at max speed $maxSpeed") } } fun main() { val car = Car() car.start() // Output: Starting car at max speed 120 // car.internalStart() // Error: Cannot access 'internalStart' // println(car.maxSpeed) // Error: Cannot access 'maxSpeed' }
通过使用实现接口的抽象类,我们可以添加 protected 成员。Car 类继承了接口和 protected 成员,同时将它们对外部代码隐藏。
protected 在密封类中
密封类通常使用 protected 构造函数来控制继承。这限制了子类化到声明密封类的文件。
package com.zetcode sealed class Result { protected constructor() class Success(val data: String) : Result() class Error(val message: String) : Result() } fun process(result: Result) { when (result) { is Result.Success -> println(result.data) is Result.Error -> println(result.message) } } fun main() { val success = Result.Success("Data loaded") val error = Result.Error("Failed to load") process(success) // Output: Data loaded process(error) // Output: Failed to load // val custom = Result() // Error: Cannot access '' }
密封类的 protected 构造函数阻止直接实例化,同时允许预定义的子类。这是限制层次结构的常见模式。
protected 在伴生对象中
伴生对象成员可以是 protected 的,使其仅对类及其子类可见。这对于共享实现细节很有用。
package com.zetcode open class Logger { protected companion object { const val PREFIX = "LOG: " fun formatMessage(message: String): String { return PREFIX + message } } fun log(message: String) { println(formatMessage(message)) } } class FileLogger : Logger() { fun logToFile(message: String) { println("Writing: ${formatMessage(message)}") } } fun main() { val logger = Logger() logger.log("Test message") // Output: LOG: Test message val fileLogger = FileLogger() fileLogger.logToFile("File message") // Output: Writing: LOG: File message // println(Logger.PREFIX) // Error: Cannot access 'PREFIX' // Logger.formatMessage("Direct") // Error: Cannot access 'formatMessage' }
protected 伴生成员在 Logger 和 FileLogger 中可访问,但不能在外部访问。这共享了实现,同时将其对用户隐藏。
protected 的最佳实践
- 谨慎使用: 仅当子类需要直接访问成员时才将成员设为 protected。
- 记录 protected 成员: 清楚地记录 protected API,因为它们是您类契约的一部分。
- 考虑替代方案: 有时组合比公开 protected 成员更好。
- 注意过度使用: 太多 protected 成员会使子类化变得复杂。
- 与其他修饰符结合使用: protected internal 对于框架代码很有用。
来源
本教程深入介绍了 Kotlin 的 protected
关键字,展示了它与属性、方法、构造函数和特殊类类型的用法。正确使用 protected 可见性有助于创建可维护的类层次结构,同时保持封装。
作者
列出 所有 Kotlin 教程。