详解: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 类型扩展详解 ------ 新手使用指南
相关推荐
xianrenli382 小时前
android特许权限调试
android
*拯5 小时前
Uniapp Android/IOS 获取手机通讯录
android·ios·uni-app
zhangphil6 小时前
Kotlin高阶函数多态场景条件判断与子逻辑
kotlin
天天打码7 小时前
Lynx-字节跳动跨平台框架多端兼容Android, iOS, Web 原生渲染
android·前端·javascript·ios
lilili啊啊啊9 小时前
iOS safari和android chrome开启网页调试与检查器的方法
android·ios·safari
Blue.ztl11 小时前
菜鸟之路day31一一MySQL之多表设计
android·数据库·mysql
练习本15 小时前
Android系统架构模式分析
android·java·架构·系统架构
每次的天空20 小时前
Kotlin 内联函数深度解析:从源码到实践优化
android·开发语言·kotlin
练习本20 小时前
Android MVC架构的现代化改造:构建清晰单向数据流
android·架构·mvc
早上好啊! 树哥20 小时前
android studio开发:设置屏幕朝向为竖屏,强制应用的包体始终以竖屏(纵向)展示
android·ide·android studio