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 重写规则
- 可见性规则 :重写方法的可见性不能低于原方法(如不能将
protected
重写为private
) - val/var规则 :可以用
var
重写val
,但不能用val
重写var
- 初始化顺序:父类初始化在子类之前
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")
}
}
七、最佳实践
- 最小开放原则 :只将需要被继承的类和需要被重写的方法标记为
open
- 谨慎使用继承:优先考虑组合而非继承
- 避免脆弱基类问题 :
- 避免在构造函数或init块中调用可被重写的方法
- 使用
final
保护关键方法
- 文档化重写契约 :使用
@Override
和注释说明重写方法的预期行为 - 接口优于抽象类:当只需要定义行为契约时
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的设计哲学:显式优于隐式,这有助于创建更可维护的代码。
更多分享
- 一文吃透Kotlin中冷流(Clod Flow)和热流(Hot Flow)
- 一文带你吃透Kotlin协程的launch()和async()的区别
- Kotlin 委托与扩展函数------新手入门
- Kotlin 作用域函数(let、run、with、apply、also)的使用指南
- 一文带你吃透Kotlin中 lateinit 和 by lazy 的区别和用法
- Kotlin 扩展方法(Extension Functions)使用详解
- Kotlin 中 == 和 === 的区别
- Kotlin 操作符与集合/数组方法详解------新手指南
- Kotlin 中 reified 配合 inline 不再被类型擦除蒙蔽双眼
- Kotlin Result 类型扩展详解 ------ 新手使用指南