kotlin抽象类(与接口的区别)

我们来详细探讨一下 Kotlin 中的抽象类。

在 Kotlin 中,抽象类是一种不能被直接实例化的类,其主要目的是作为其他类的基类(父类),用于定义通用的属性和方法,而将具体的实现细节留给子类。

1. 定义抽象类

在 Kotlin 中,使用 abstract 关键字来声明一个抽象类。

kotlin

kotlin 复制代码
// 定义一个抽象类 Shape
abstract class Shape {
    // 抽象属性(没有初始化值,必须在子类中重写)
    abstract val name: String

    // 抽象方法(没有方法体,必须在子类中重写)
    abstract fun calculateArea(): Double

    // 非抽象方法(有具体实现,可以被子类继承或重写)
    fun printName() {
        println("形状名称: $name")
    }
}

关键点

  • 抽象类可以包含抽象属性抽象方法,这些必须在子类中实现。
  • 抽象类也可以包含非抽象属性非抽象方法,这些会被子类继承。
  • 抽象类不能被直接实例化 ,即不能使用 val shape = Shape()

2. 继承抽象类

子类继承抽象类时,必须使用 override 关键字重写所有抽象属性和方法。

kotlin

kotlin 复制代码
// 子类 Circle 继承自抽象类 Shape
class Circle(
    val radius: Double
) : Shape() {
    // 重写抽象属性
    override val name: String = "圆形"

    // 重写抽象方法
    override fun calculateArea(): Double {
        return Math.PI * radius * radius
    }
}

// 子类 Rectangle 继承自抽象类 Shape
class Rectangle(
    val width: Double,
    val height: Double
) : Shape() {
    override val name: String = "矩形"

    override fun calculateArea(): Double {
        return width * height
    }
}

使用示例

kotlin

scss 复制代码
fun main() {
    val circle = Circle(5.0)
    circle.printName() // 输出:形状名称: 圆形
    println("面积: ${circle.calculateArea()}") // 输出:面积: 78.5398...

    val rectangle = Rectangle(4.0, 6.0)
    rectangle.printName() // 输出:形状名称: 矩形
    println("面积: ${rectangle.calculateArea()}") // 输出:面积: 24.0
}

3. 抽象类的特点

  1. 不能实例化

    kotlin

    scss 复制代码
    val shape = Shape() // 错误:抽象类不能被实例化
  2. 可以包含抽象和非抽象成员

    • 抽象成员(abstract 修饰)必须在子类中重写。
    • 非抽象成员可以直接继承或被子类重写(使用 override)。
  3. 子类必须实现所有抽象成员:如果子类没有实现所有抽象属性和方法,那么子类也必须声明为抽象类。

    kotlin

    kotlin 复制代码
    abstract class Square(
        val sideLength: Double
    ) : Shape() {
        // 只重写了抽象属性,没有重写抽象方法 calculateArea()
        override val name: String = "正方形"
        
        // 因此 Square 类也必须是抽象的
    }
  4. 抽象类可以继承其他类:抽象类可以继承自另一个非抽象类或抽象类。

    kotlin

    kotlin 复制代码
    open class Animal {
        open fun makeSound() {
            println("动物发出声音")
        }
    }
    
    abstract class Dog : Animal() {
        override abstract fun makeSound() // 重写并声明为抽象方法,子类必须实现
    }
    
    class Puppy : Dog() {
        override fun makeSound() {
            println("小狗汪汪叫")
        }
    }

4. 抽象类与接口的区别

在 Kotlin 中,抽象类和接口(interface)都可以用于定义抽象行为,但它们有几个关键区别:

特性 抽象类 (abstract class) 接口 (interface)
构造函数 可以有构造函数 不能有构造函数(Kotlin 1.9+ 支持接口中定义带默认实现的属性,但仍不能有构造函数)
多重继承 只能单继承(一个类只能继承一个抽象类) 可以实现多个接口
属性 可以包含非抽象属性 接口中的属性默认是抽象的,或必须提供 getter 实现
方法实现 可以包含非抽象方法的实现 接口中的方法默认是抽象的,或必须提供实现(Kotlin 1.4+ 支持接口方法的默认实现)
访问修饰符 可以使用 private, protected, public 接口成员默认是 public,不能有 private 修饰符

使用建议

  • 如果需要定义一个通用的基类,并且需要包含构造函数、非抽象属性或方法,使用抽象类
  • 如果只是需要定义一组抽象行为(方法或属性),并且希望类可以实现多个这样的行为,使用接口

5. 总结

  • 抽象类是用 abstract 关键字声明的类,不能被直接实例化。
  • 抽象类可以包含抽象成员(必须在子类中重写)和非抽象成员(可以直接继承)。
  • 子类继承抽象类时,必须重写所有抽象成员,否则子类也必须是抽象类。
  • 抽象类适合作为具有共同属性和行为的类的基类,而接口适合定义多个类可以实现的抽象行为。
相关推荐
sino爱学习1 小时前
Arthas 线上常用命令速查手册:Java 诊断神器,5 分钟定位线上问题!
后端
songroom1 小时前
Rust: 量化策略回测与简易线程池构建(MPMC)
开发语言·后端·rust
绝无仅有2 小时前
面试日志elk之ES数据查询与数据同步
后端·面试·架构
码农BookSea2 小时前
用好PowerMock,轻松搞定那些让你头疼的单元测试
后端·单元测试
绝无仅有2 小时前
大场面试之最终一致性与分布式锁
后端·面试·架构
晨晖23 小时前
springboot的Thymeleaf语法
java·spring boot·后端
seven97_top3 小时前
SpringCloud 常见面试题(二)
后端·spring·spring cloud
b***66613 小时前
【springboot】健康检查 监控
java·spring boot·后端
databook4 小时前
让你的动画“活”过来:Manim 节奏控制指南 (Rate Functions)
后端·python·动效