ZetCode

Kotlin 抽象关键字

最后修改于 2025 年 4 月 19 日

Kotlin 的 `abstract` 关键字用于创建不能直接实例化的抽象类和成员。抽象成员必须由具体的子类实现。本教程通过实际例子深入探讨抽象类和成员。

基本定义

Kotlin 中的 `abstract` 关键字声明抽象类或成员。抽象类不能直接实例化。它们用作具体实现的基类。抽象成员没有实现,必须在子类中被重写。

基本抽象类

抽象类用 `abstract` 关键字声明。它可以包含抽象成员和具体成员。抽象类不能直接实例化。

BasicAbstract.kt
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 属性。

AbstractProperty.kt
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 属性。

带有具体成员的抽象类

抽象类可以包含抽象成员和具体成员。具体成员提供默认实现,子类可以使用或重写这些实现。

MixedAbstract.kt
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() 方法。

多个抽象成员

一个抽象类可以有多个抽象成员。子类必须实现所有这些成员。这确保了所有实现之间的一致接口。

MultipleAbstract.kt
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 子类都具有一致的行为,同时允许实现细节有所不同。

抽象类继承

抽象类可以继承自其他抽象类。子抽象类不需要实现所有父抽象成员。它可以添加新的抽象成员。

AbstractInheritance.kt
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 实现了来自这两个类的所有抽象成员。这展示了抽象类如何相互构建。

带有接口的抽象类

抽象类可以实现接口。它们可以为某些接口成员提供实现,而将其他成员保留为抽象。这在设计中提供了灵活性。

AbstractWithInterface.kt
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 实现了剩余的抽象成员。这展示了接口的部分实现。

抽象类作为参数类型

抽象类可以用作函数中的参数类型。这允许多态行为,其中任何具体的子类都可以作为参数传递。

AbstractAsParameter.kt
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 抽象类文档

本教程深入探讨了 Kotlin 的 `abstract` 关键字,展示了如何创建抽象类和成员。我们探索了各种场景,包括属性、继承和接口实现。正确使用抽象类可以创建灵活且可维护的类层次结构。

作者

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

列出 所有 Kotlin 教程