kotlin 继承、接口和扩展

本文对比了Kotlin与Java中的继承与重写特性,详细解析了Kotlin中如何使用open关键字进行类与方法的重写,以及如何在构造函数中调用父类构造方法。同时,介绍了Kotlin中接口的实现方式与扩展函数的概念。

Java 中,所有的对象都默认继承 Object,里面有几个默认的方法可以供我们重写,常见的有 equals()、hashCode()、toString() 等方法;在 Kotlin 中,所有的对象默认继承 Any,

public open class Any {
    public open operator fun equals(other: Any?): Boolean

    public open fun hashCode(): Int

    public open fun toString(): String
}

它里面有三个方法可以供我们重写。Java 中的类,默认是可以继承的,而 Kotlin 中默认则是不能继承的,注意 open 这个关键字,有它修饰才可以继承,不管是类还是方法,或是属性值。

open class A {
    open fun a () { print("A") }

}

class B : A(){
    override fun a() {
        print("B")
    }
}

这里,继承使用 : 来表示,单独看这个看不出什么名堂,现在稍作调整

open class A1(name: String) {
    var name = name
    open fun a () { print("A") }
    constructor() : this("a1")
}

class B1 : A1{
    constructor() : super("b1")
    override fun a() {
        print("B1")
    }
}

在这个例子中,A1 的构造方法为包含一个参数的构造,这点写法和 Java 中不一样,并且直接在 class 后面添加括号的构造方法为主构造,如果没参数,可以写个空括号,或者省略括号,这样就默认为无参构造。A1 中的 constructor() 为辅助构造方法,它必须调用主构造方法,就如上面写的一样。B1 中继承了 A1,此时柱构造后面跟的是 A1 的空构造,子类必须在构造方法中调用父类的主构造方法,因此,在 B1 的里面就有了 constructor() : super("b1") ,上述可以简化为

class B2 : A1("b2"){
    override fun a() {
        print("B2")
    }
}

直接在 B2 的主构造后面跟随 A1 的主构造方法,我们再看看下面的一种情况

class B3 : A1{
    constructor(name: String) : super(name)
    constructor(name: String, age : Int) : this(name)
    override fun a() {
        print("B3")
    }
}

B3 中,内部的辅助构造方法,最终必须调用到父类的主构造方法,就如 B3 中的两个参数构造方法调用了一个参数的构造,一个参数的构造调用了父类的一个参数的构造方法。再比如一个实体类继承了一个抽象类和一个接口,抽象类和接口恰好有相同的抽象方法,在 java  中,子类只能重写一个,但在 kotlin 中,重写后还是可以调用父类的方法

open class AA {
    open fun ss () { println("A") }
    fun a() { println("a") }
}

interface BB {
    fun ss() { println("B") }
    fun b() { println("b") }
}

class CC : AA(), BB{
    override fun ss() {
        super<AA>.ss()//调用 AA.ss()
        super<BB>.ss()//调用 BB.ss()
    }
}

fun test(){
    val c =  CC()
    c.ss()
}

运行后,打印的值为

A
B


在java的 jdk 1.8 中,接口还是只能定义抽象方法和变量,不能定义可以重写的非抽象方法,但在 kotlin 中,不能定义常量,可以定义抽象和非抽象方法,举个例子

interface A {
    var name:String //name 属性, 抽象的
    fun start() { println("start A") }   // 已实现
    fun end()                  // 未实现
}

interface B {
    fun start() { println("start B") }   // 已实现
    fun end() { println("end B") } // 已实现
}

class C : A {
    override var name: String = "C" //重写属性
    override fun end() { println("end C") }   // 重写
}

class D : A, B {
    override var name: String = "D" //重写属性
    override fun start() {
        super<A>.start()
        super<B>.start()
    }

    override fun end() {
        super<B>.end()
        println(name)
    }
}

fun test1(){
    val d =  D()
    d.start()
    d.end()
}

打印结构为

start A
start B
end B
D


Kotlin 比着 Java 多了一个扩展函数的概念,什么意思呢?看个例子

class A{
    fun test(){
        println(" test  A ")
    }
}

fun A.too(){
    println(" too  A ")
}

fun function(){
    var a = A()
    a.test()
    a.too()
}

在这个例子中,定义了对象 A,A 中有个 test() 方法;上面的 A.too() 就是扩展的方法,它是个静态方法,优先于对象的存在;我们调用 function() 方法,打印数据如下

 test  A
 too  A


扩展的是静态的方法,但如果我们想直接调用,如下

class AA{
    fun test(){
        println(" test  AA ")
    }
    companion object
}

fun AA.too(){
    println(" too  AA ")
}

fun AA.Companion.to(){
    println(" to  AA ")
}

fun function(){
    var aa = AA()
    aa.test()
    aa.too()
    AA.to()
}

调用 function() 方法,打印数据为

 test  AA
 too  AA
 to  AA


 

<think> 嗯,用户想了解Kotlin接口的使用方法特性。这个问题其实挺有意思的,因为Kotlin接口设计确实比Java更灵活。用户应该是个安卓开发者,或者正在学习Kotlin,想深入理解语言特性。 从引用内容看,用户提供的资料已经点出了两个关键特性:接口方法的默认实现抽象属性。不过我需要更系统地整理。首先想到接口Kotlin中的核心功能是定义契约,但比Java多了些现代语言的特性。 用户可能特别关注实际用法,所以最好用代码示例说明。比如接口里定义属性时,子类必须覆盖抽象属性,但可以继承带getter的实现属性。还有那个fly()的默认方法实现,在Java8之前是完全做不到的。 对了,接口的多继承问题也很重要。Kotlin允许实现多个接口,如果出现同名方法冲突怎么处理?这个需要特别说明override规则。还有接口抽象类的区别,虽然引用里提到可以结合使用,但用户可能更想知道何时该用哪个。 用户没问但可能隐含的需求是:为什么要设计这些特性?其实就是为了解决Java接口的局限性。比如Java里要用默认方法得等到Java8,属性更是完全不能定义。Kotlin这些设计让接口真正具备了行为抽象的能力,而不仅是形状描述。 我突然想到个易错点:接口里的属性本质上都是抽象的,即使有getter也只是默认实现,子类仍然可以覆盖。这个在示例里要明确标注出来。还有初始化的问题,接口属性不能直接初始化,只能通过getter间接实现。 最后得提醒下使用场景。比如团队协作时,用接口默认方法能减少样板代码;定义SDK契约时,带默认方法的接口比抽象类更灵活,因为不限制继承关系。不过复杂状态管理还是抽象类更合适。 </think> 好的,我们来详细梳理一下 Kotlin 接口的使用方法特性。Kotlin接口在 Java 的基础上做了重要增强,使其功能更加强大灵活。 **核心特性与使用方法:** 1. **定义契约与方法声明:** * Java 一样,Kotlin 接口的核心作用是定义实现类必须遵守的*契约*(合同)。 * 接口中可以声明抽象方法,这些方法**没有方法体**。实现该接口的类**必须**提供这些抽象方法的具体实现。 * **语法示例:** ```kotlin interface Driveable { fun startEngine() // 抽象方法,无实现 fun accelerate() // 抽象方法,无实现 fun brake() // 抽象方法,无实现 } ``` 2. **方法默认实现 (Kotlin 的核心增强):** * **特性:** Kotlin 接口允许为**方法**提供**默认实现**。这是 Kotlin 相对于早期 Java 版本接口的一个巨大优势(Java 8 引入了类似概念 `default` 方法)[^1]。 * **作用:** 它允许在不破坏现有实现类的情况下,向接口添加新功能。实现类可以选择使用默认实现,也可以选择覆盖(override)它来提供自己的特定行为。 * **语法示例:** ```kotlin interface Playable { fun play() // 抽象方法 fun stop() { // 带默认实现的方法 println("Playback stopped.") } } class MusicPlayer : Playable { override fun play() { // 必须实现抽象方法 println("Playing music...") } // stop() 方法可以不实现,将使用接口提供的默认实现 } class VideoPlayer : Playable { override fun play() { println("Playing video...") } override fun stop() { // 覆盖默认实现 println("Video playback stopped abruptly.") } } ``` 3. **抽象属性:** * **特性:** Kotlin 接口可以声明**抽象属性**。这意味着实现类必须提供该属性的具体值或实现其访问器(getter/setter)[^1]。 * **作用:** 强制实现类拥有某个特定状态特征。 * **语法示例:** ```kotlin interface Named { val name: String // 抽象属性 } class Person(override val name: String) : Named // 通过主构造函数提供实现 class Company : Named { override val name: String // 在类体中提供实现 get() = "Awesome Corp" } ``` 4. **属性访问器实现 (getter):** * **特性:** Kotlin 接口可以为属性提供自定义的 `get()` 访问器实现。这使得接口能够提供具有默认计算的属性。 * **注意:** 接口中的属性本身**不能有幕后字段**(`field`)。因此,它们的值必须是基于其他属性计算出来的常量,或者依赖于实现类提供的具体状态(通过抽象属性或方法间接获得)。 * **语法示例:** ```kotlin interface Shape { val area: Double // 抽象属性,需要具体实现提供计算或存储 } interface Rectangular { val width: Double val height: Double val area: Double // 这个属性可以在接口中提供默认 get() get() = width * height // 利用抽象属性 width height 计算默认值 } class Rectangle(override val width: Double, override val height: Double) : Rectangular { // area 属性使用接口默认的 get() 实现,无需显式覆盖 } ``` 5. **接口继承:** * 接口可以继承(`extends`)一个或多个其他接口。 * 实现类需要满足其自身实现的接口及其所有父接口定义的所有契约。 * **语法示例:** ```kotlin interface Flyable { fun fly() } interface Powered : Flyable { // Powered 接口继承自 Flyable val enginePower: Int fun refuel() } class Airplane : Powered { override val enginePower: Int = 1000 override fun refuel() { println("Refueling...") } override fun fly() { println("Flying with power $enginePower...") } // 必须实现 Flyable 的 fly() } ``` 6. **类实现多个接口:** * Kotlin 支持一个类**实现多个接口**(`class MyClass : InterfaceA, InterfaceB`)。 * 这是实现多重行为继承的主要方式(Kotlin 不支持类的多重继承)。 * **处理冲突:** 如果多个父接口声明了**相同签名**(相同名称、相同参数列表)的**带默认实现的方法**,实现类**必须显式覆盖**该方法,并通过 `super<InterfaceName>.methodName()` 语法明确指定调用哪个父接口的实现(或提供全新的实现)。 * **语法示例:** ```kotlin interface A { fun foo() { println("A foo") } } interface B { fun foo() { println("B foo") } } class C : A, B { override fun foo() { super<A>.foo() // 调用 A 的 foo 实现 super<B>.foo() // 调用 B 的 foo 实现 // 或者只调用一个,或者提供完全不同的实现 println("C's own foo") } } ``` **接口 vs 抽象类:** 引用[2]中提到接口抽象类可以结合使用。理解它们的区别有助于选择正确的工具: 1. **状态存储:** * **抽象类:** 可以有**构造函数参数****幕后字段**(`field`)来存储状态(声明 `var` / `val` 属性并初始化)。 * **接口:** **不能有构造函数**,其中的属性**不能有幕后字段**(不能在接口内直接初始化属性或在自定义访问器中使用 `field`)。接口属性的值要么是抽象的(由实现类提供),要么是基于其他抽象成员计算出来的(通过 `get()`)。 2. **初始化逻辑:** * **抽象类:** 可以有 `init` 块构造函数来执行初始化逻辑。 * **接口:** **没有初始化逻辑块**。 3. **成员可见性:** * **抽象类:** 成员可以有 `public`、`protected`、`private` (默认是 `public`) 等可见性修饰符。 * **接口:** 所有成员**默认是 `public` 且 `open`**(可覆盖)。不能声明为 `private`(在 Kotlin 1.4 之前,不能有 `protected`,之后可以在接口内部使用 `protected`,但对实现类来说仍是 `public` 的)。 4. **设计目的:** * **接口:** 侧重于定义**行为契约**(*can-do* 关系),强调“做什么”。通过默认方法提供基础实现,通过抽象方法属性强制实现细节。是实现多重继承的首选机制。 * **抽象类:** 侧重于定义**类层次结构的基础部分**(*is-a* 关系),强调“是什么”。它可以包含状态、初始化逻辑部分实现,用于封装子类共有的状态行为逻辑。一个类只能继承一个抽象类。 **总结 Kotlin 接口的关键特点:** * **强制契约:** 通过抽象方法抽象属性定义必须实现的部分。 * **默认方法:** 提供方法的默认实现,增强接口扩展能力,减少样板代码[^1]。 * **抽象属性:** 可以声明需要实现类提供的属性状态[^1]。 * **属性 `get()` 实现:** 可以为属性提供基于其他抽象成员计算的默认值访问器。 * **接口继承:** 接口可以继承其他接口。 * **多重实现:** 类可以实现多个接口(多重继承行为)。 * **解决多重继承冲突:** 当多个父接口有相同签名的带默认实现的方法时,强制要求实现类明确覆盖并指定调用哪个父版本的实现。 * **与抽象类互补:** 接口定义行为契约,抽象类定义共享状态部分实现,两者可以结合使用构建灵活的类层次结构[^2]。 **
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值