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 教程。