Kotlin 抽象关键字
最后修改于 2025 年 4 月 19 日
Kotlin 的 `abstract` 关键字用于创建不能直接实例化的抽象类和成员。抽象成员必须由具体的子类实现。本教程通过实际例子深入探讨抽象类和成员。
基本定义
Kotlin 中的 `abstract` 关键字声明抽象类或成员。抽象类不能直接实例化。它们用作具体实现的基类。抽象成员没有实现,必须在子类中被重写。
基本抽象类
抽象类用 `abstract` 关键字声明。它可以包含抽象成员和具体成员。抽象类不能直接实例化。
package com.zetcode abstract class Shape { abstract fun area(): Double } class Circle(val radius: Double) : Shape() { override fun area(): Double { return Math.PI * radius * radius } } fun main() { val circle = Circle(5.0) println("Area: ${circle.area()}") // Output: Area: 78.53981633974483 }
这里我们定义了一个抽象的 Shape 类,它有一个抽象的 area() 函数。Circle 类继承自 Shape,并提供了 area() 的具体实现。然后我们创建一个 Circle 实例并计算它的面积。
抽象属性
除了方法之外,抽象类还可以有抽象属性。这些属性必须由具体的子类实现。它们可以是 val 或 var 属性。
package com.zetcode abstract class Animal { abstract val sound: String abstract var age: Int } class Dog : Animal() { override val sound = "Woof" override var age = 0 } fun main() { val dog = Dog() println("Sound: ${dog.sound}, Age: ${dog.age}") // Output: Sound: Woof, Age: 0 dog.age = 3 println("New age: ${dog.age}") // Output: New age: 3 }
Animal 类声明了抽象的 sound (val) 和 age (var) 属性。Dog 类提供了具体的实现。我们可以读取这两个属性,并修改可变的 age 属性。
带有具体成员的抽象类
抽象类可以包含抽象成员和具体成员。具体成员提供默认实现,子类可以使用或重写这些实现。
package com.zetcode abstract class Vehicle { abstract val maxSpeed: Double fun start() { println("Vehicle started") } abstract fun stop() } class Car : Vehicle() { override val maxSpeed = 200.0 override fun stop() { println("Car stopped") } } fun main() { val car = Car() car.start() // Output: Vehicle started println("Max speed: ${car.maxSpeed}") // Output: Max speed: 200.0 car.stop() // Output: Car stopped }
Vehicle 类同时具有抽象成员(maxSpeed, stop())和具体成员(start())。Car 类继承自 Vehicle 并实现了抽象成员,同时原样使用了具体的 start() 方法。
多个抽象成员
一个抽象类可以有多个抽象成员。子类必须实现所有这些成员。这确保了所有实现之间的一致接口。
package com.zetcode abstract class Employee { abstract val name: String abstract val salary: Double abstract fun work() abstract fun takeBreak() } class Developer : Employee() { override val name = "John Doe" override val salary = 75000.0 override fun work() { println("Writing code...") } override fun takeBreak() { println("Taking coffee break") } } fun main() { val dev = Developer() println("${dev.name} earns ${dev.salary}") // Output: John Doe earns 75000.0 dev.work() // Output: Writing code... dev.takeBreak() // Output: Taking coffee break }
Employee 抽象类定义了四个抽象成员。Developer 类为它们都提供了实现。这确保了所有 Employee 子类都具有一致的行为,同时允许实现细节有所不同。
抽象类继承
抽象类可以继承自其他抽象类。子抽象类不需要实现所有父抽象成员。它可以添加新的抽象成员。
package com.zetcode abstract class Person { abstract val name: String abstract fun greet() } abstract class Student : Person() { abstract val studentId: Int abstract fun study() } class CollegeStudent : Student() { override val name = "Alice" override val studentId = 12345 override fun greet() { println("Hi, I'm $name") } override fun study() { println("Studying hard...") } } fun main() { val student = CollegeStudent() student.greet() // Output: Hi, I'm Alice println("ID: ${student.studentId}") // Output: ID: 12345 student.study() // Output: Study hard... }
Person 是一个具有抽象成员的抽象类。Student 继承自 Person 并添加新的抽象成员。CollegeStudent 实现了来自这两个类的所有抽象成员。这展示了抽象类如何相互构建。
带有接口的抽象类
抽象类可以实现接口。它们可以为某些接口成员提供实现,而将其他成员保留为抽象。这在设计中提供了灵活性。
package com.zetcode interface Drawable { fun draw() fun resize(scale: Double) } abstract class Shape : Drawable { abstract val color: String override fun resize(scale: Double) { println("Resizing by scale $scale") } } class Circle : Shape() { override val color = "Red" override fun draw() { println("Drawing a $color circle") } } fun main() { val circle = Circle() circle.draw() // Output: Drawing a Red circle circle.resize(1.5) // Output: Resizing by scale 1.5 }
Shape 是一个实现 Drawable 接口的抽象类。它为 resize() 提供了具体实现,但将 draw() 保留为抽象。Circle 实现了剩余的抽象成员。这展示了接口的部分实现。
抽象类作为参数类型
抽象类可以用作函数中的参数类型。这允许多态行为,其中任何具体的子类都可以作为参数传递。
package com.zetcode abstract class Logger { abstract fun log(message: String) } class ConsoleLogger : Logger() { override fun log(message: String) { println("CONSOLE: $message") } } class FileLogger : Logger() { override fun log(message: String) { println("FILE: $message (writing to file)") } } fun process(logger: Logger, message: String) { logger.log(message) } fun main() { val consoleLogger = ConsoleLogger() val fileLogger = FileLogger() process(consoleLogger, "Test message") // Output: CONSOLE: Test message process(fileLogger, "Important data") // Output: FILE: Important data (writing to file) }
`process` 函数接受任何 Logger 子类。我们传递了不同的日志记录器实现(ConsoleLogger 和 FileLogger),每个实现都以不同的方式处理日志记录。这演示了使用抽象类的多态性。
抽象类的最佳实践
- 用于公共行为: 当多个类共享可以实现一次的公共行为时,抽象类是理想的选择。
- 对于简单的契约,优先使用接口: 对于没有共享实现的简单方法契约,优先使用接口。
- 记录抽象成员: 清楚地记录抽象成员的目的和预期行为。
- 保持抽象类专注: 抽象类应该有一个单一的、定义明确的责任。
- 考虑密封类: 对于受限的类层次结构,考虑使用密封类而不是抽象类。
来源
本教程深入探讨了 Kotlin 的 `abstract` 关键字,展示了如何创建抽象类和成员。我们探索了各种场景,包括属性、继承和接口实现。正确使用抽象类可以创建灵活且可维护的类层次结构。
作者
列出 所有 Kotlin 教程。