详解:Kotlin 类的继承与方法重载

Kotlin 的继承系统和方法重载机制既保留了面向对象编程的核心概念,又通过一些独特的设计提高了安全性和表达力。本文将从多个方面详细解析这些特性。

一、类继承基础

1.1 开放原则(Open Principle)

Kotlin 默认所有类都是 final 的(不可继承),必须明确使用 open 关键字标记可继承的类:

kotlin 复制代码
open class Animal(val name: String) {  // 必须声明为open才能被继承
    open fun makeSound() {            // 必须声明为open才能被重写
        println("Some generic animal sound")
    }
}

class Dog(name: String) : Animal(name) {  // 使用:表示继承
    override fun makeSound() {            // 必须使用override关键字
        println("$name says: Woof!")
    }
}

fun main() {
    val dog = Dog("Buddy")
    dog.makeSound()  // 输出: Buddy says: Woof!
}

1.2 构造函数继承

主构造函数继承:

kotlin 复制代码
open class Person(val name: String, val age: Int)

// 子类主构造函数必须初始化父类
class Student(name: String, age: Int, val studentId: String) : Person(name, age)

// 使用:
val student = Student("Alice", 20, "S12345")

次构造函数继承:

kotlin 复制代码
open class View {
    constructor(ctx: Context) { /*...*/ }
    constructor(ctx: Context, attr: AttributeSet) { /*...*/ }
}

class MyView : View {
    // 使用super调用父类构造函数
    constructor(ctx: Context) : super(ctx)
    constructor(ctx: Context, attr: AttributeSet) : super(ctx, attr)
}

二、方法重载(Override)详解

2.1 基本方法重写

kotlin 复制代码
open class Shape {
    open fun draw() { println("Drawing a shape") }
    fun fill() { println("Filling the shape") }  // 默认final,不能重写
}

class Circle : Shape() {
    override fun draw() {  // 必须使用override关键字
        println("Drawing a circle")
        super.draw()       // 可选调用父类实现
    }
    
    // 不能重写fill(),因为它不是open的
}

2.2 属性重写

kotlin 复制代码
open class Vehicle {
    open val speed: Int = 100
    open val wheels: Int get() = 4  // 自定义getter的属性
}

class Bicycle : Vehicle() {
    override val speed: Int = 30    // 用val覆盖val
    override val wheels: Int = 2    // 用存储属性覆盖计算属性
}

class ElectricBike : Vehicle() {
    override var speed: Int = 50    // 可以用var覆盖val(反之不行)
}

2.3 重写规则

  1. 可见性规则 :重写方法的可见性不能低于原方法(如不能将protected重写为private
  2. val/var规则 :可以用var重写val,但不能用val重写var
  3. 初始化顺序:父类初始化在子类之前
kotlin 复制代码
open class Base {
    open val value: Int = 100
    init { println("Base init: $value") }  // 输出0,因为子类还未初始化
}

class Derived : Base() {
    override val value: Int = 200
    init { println("Derived init: $value") }
}

fun main() {
    Derived()
    // 输出:
    // Base init: 0
    // Derived init: 200
}

三、抽象类与接口实现

3.1 抽象类

kotlin 复制代码
abstract class Polygon {
    abstract fun draw()  // 抽象方法必须被实现
    open fun move() { println("Moving") }  // 可被重写的具体方法
    fun fill() { println("Filling") }      // 不可重写的具体方法
}

class Rectangle : Polygon() {
    override fun draw() { println("Drawing rectangle") }
    override fun move() { println("Rectangle moving") }
}

3.2 接口实现

kotlin 复制代码
interface Clickable {
    fun click()                  // 抽象方法
    fun showOff() = println("I'm clickable!")  // 带默认实现的方法
}

interface Focusable {
    fun showOff() = println("I'm focusable!")
}

class Button : Clickable, Focusable {
    override fun click() = println("Button clicked")
    
    // 必须重写冲突的默认实现
    override fun showOff() {
        super<Clickable>.showOff()  // 调用特定父接口的实现
        super<Focusable>.showOff()
        println("Button showing off!")
    }
}

四、方法重载(Overload)与继承

方法重载是指在同一个类中有多个同名方法,但参数不同:

kotlin 复制代码
class Calculator {
    fun add(a: Int, b: Int): Int = a + b
    fun add(a: Double, b: Double): Double = a + b  // 重载
    fun add(vararg numbers: Int): Int = numbers.sum()
    
    // 不能仅通过返回类型不同来重载
    // fun add(a: Int, b: Int): Double = (a + b).toDouble() // 编译错误
}

fun main() {
    val calc = Calculator()
    println(calc.add(2, 3))         // 调用Int版本
    println(calc.add(2.5, 3.7))     // 调用Double版本
    println(calc.add(1, 2, 3, 4)))  // 调用可变参数版本
}

继承中的重载

kotlin 复制代码
open class Printer {
    open fun print(message: String) { println("Printing: $message") }
}

class AdvancedPrinter : Printer() {
    // 重写父类方法
    override fun print(message: String) { 
        println("Advanced printing: $message") 
    }
    
    // 新增重载方法
    fun print(message: String, copies: Int) {
        repeat(copies) { print(message) }  // 调用自己的print(String)
    }
}

fun main() {
    val printer = AdvancedPrinter()
    printer.print("Hello")          // 调用重写的方法
    printer.print("Kotlin", 3)     // 调用重载的方法
}

五、特殊场景处理

5.1 禁止重写

使用 final 关键字禁止进一步重写:

kotlin 复制代码
open class Parent {
    open fun method1() { println("Parent.method1") }
    final fun method2() { println("Parent.method2") }  // 不能被子类重写
}

class Child : Parent() {
    override fun method1() { println("Child.method1") }
    // 不能重写method2
}

5.2 属性初始化陷阱

kotlin 复制代码
open class Base {
    open val value: String = "Base"
    init { println("Base init: $value") }  // 输出null(如果子类重写)
}

class Derived : Base() {
    override val value: String = "Derived"
    init { println("Derived init: $value") }
}

fun main() {
    Derived()
    // 输出:
    // Base init: null
    // Deriverd init: Derived
}

5.3 内部类与继承

kotlin 复制代码
open class Outer {
    private val secret = 42
    open inner class Inner {
        open fun showSecret() { println(secret) }
    }
}

class SubOuter : Outer() {
    inner class SubInner : Inner() {
        override fun showSecret() { 
            super.showSecret()
            println("And more!") 
        }
    }
}

fun main() {
    val inner = SubOuter().SubInner()
    inner.showSecret()  // 输出: 42 \n And more!
}

六、与Java的互操作性

6.1 Java类继承Kotlin类

kotlin 复制代码
// Kotlin基类
open class KBase(val name: String) {
    open fun show() { println("KBase: $name") }
}

// Java子类
/*
public class JDerived extends KBase {
    public JDerived(String name) {
        super(name);
    }
    
    @Override
    public void show() {
        System.out.println("JDerived: " + getName());
    }
}
*/

6.2 Kotlin类继承Java类

java 复制代码
// Java基类
public class JBase {
    public void method() { System.out.println("JBase.method"); }
}
kotlin 复制代码
// Kotlin子类
class KDerived : JBase() {
    override fun method() {  // 可以重写Java方法
        super.method()
        println("KDerived.method")
    }
}

6.3 注解使用

kotlin 复制代码
open class AnnotatedBase {
    @JvmOverloads  // 为Java生成重载方法
    open fun foo(bar: Int = 0) { println("Bar: $bar") }
}

class AnnotatedDerived : AnnotatedBase() {
    @JvmName("customFoo")  // 改变JVM名称
    override fun foo(bar: Int) {
        println("Overridden bar: $bar")
    }
}

七、最佳实践

  1. 最小开放原则 :只将需要被继承的类和需要被重写的方法标记为open
  2. 谨慎使用继承:优先考虑组合而非继承
  3. 避免脆弱基类问题
    • 避免在构造函数或init块中调用可被重写的方法
    • 使用final保护关键方法
  4. 文档化重写契约 :使用@Override和注释说明重写方法的预期行为
  5. 接口优于抽象类:当只需要定义行为契约时
kotlin 复制代码
// 良好实践示例
abstract class AbstractDocument {
    abstract fun serialize(): String
    
    // 保护方法,禁止重写
    protected final fun validate() { /*...*/ }
    
    // 模板方法
    fun save() {
        validate()
        val data = serialize()
        // 保存逻辑...
    }
}

class JsonDocument : AbstractDocument() {
    override fun serialize(): String {
        return "{...}"  // JSON序列化实现
    }
}

八、对比表

特性 继承 方法重载
关键字 open class, override 同名不同参
默认行为 类和方法默认final 允许在同一类中
多态支持 支持运行时多态 编译时静态分派
访问控制 子类可见性不能比父类更严格 无特殊限制
与Java区别 需要显式open,Java默认非final 概念相同
典型应用场景 扩展/特化父类功能 提供相同功能的多种参数形式

通过合理使用Kotlin的继承和方法重载机制,可以构建出既灵活又安全的面向对象系统。记住Kotlin的设计哲学:显式优于隐式,这有助于创建更可维护的代码。

更多分享

  1. 一文吃透Kotlin中冷流(Clod Flow)和热流(Hot Flow)
  2. 一文带你吃透Kotlin协程的launch()和async()的区别
  3. Kotlin 委托与扩展函数------新手入门
  4. Kotlin 作用域函数(let、run、with、apply、also)的使用指南
  5. 一文带你吃透Kotlin中 lateinit 和 by lazy 的区别和用法
  6. Kotlin 扩展方法(Extension Functions)使用详解
  7. Kotlin 中 == 和 === 的区别
  8. Kotlin 操作符与集合/数组方法详解------新手指南
  9. Kotlin 中 reified 配合 inline 不再被类型擦除蒙蔽双眼
  10. Kotlin Result 类型扩展详解 ------ 新手使用指南
相关推荐
顾林海21 分钟前
深度解析LinkedHashMap工作原理
android·java·面试
JasonYin23 分钟前
Git提交前缀
android
louisgeek1 小时前
Android 类加载机制
android
碎风,蹙颦1 小时前
Android开发过程中遇到的SELINUX权限问题
android·人工智能
HZW89701 小时前
鸿蒙应用开发—数据持久化之SQLite
android·前端·harmonyos
百锦再1 小时前
Android Studio 日志系统详解
android·java·ide·app·android studio·安卓·idea
好学人1 小时前
Kotlin中的作用域关键字
kotlin
fatiaozhang95272 小时前
晶晨线刷工具下载及易错点说明:Key文件配置错误/mac剩余数为0解决方法
android·电视盒子·魔百盒刷机
QING6185 小时前
Kotlin 伴生对象(Companion Object)详解 —— 使用指南
android·kotlin·app