《Kotlin从小白到大牛》第13章:抽象类与接口

第13章 抽象类与接口

设计良好的软件系统应该具备“可复用性”和“可扩展性”,能够满足用户需求的不断变更。使用抽象类和接口是实现“可复用性”和“可扩展性”重要的设计手段。

13.1 抽象类

Kotlin语言提供了两种类:一种是具体类;另一种是抽象类。前面章节接触的类都是具体类。这一节介绍一下抽象类。

13.1.1 抽象类概念
在12.4.1节介绍多态时,使用过几何图形类示例,其中Figure(几何图形)类中有一个onDraw(绘图)函数,Figure有两个子类Ellipse(椭圆形)和Triangle(三角形),Ellipse和Triangle重写onDraw函数。
作为父类Figure(几何图形)并不知道在实际使用时有多少个子类,目前有椭圆形和三角形,那么不同的用户需求可能会有矩形或圆形等其他几何图形,而onDraw函数只有确定是哪一个子类后才能具体实现。Figure中的onDraw函数不能具体实现,所以只能是一个抽象函数。在Kotlin中具有抽象函数的类称为“抽象类”,Figure是抽象类,其中的onDraw函数是抽象函数。如图13-1所示类图中Figure是抽象类,Ellipse和Triangle是Figure子类实现Figure的抽象函数onDraw。
在这里插入图片描述
13.1.2 抽象类声明和实现
在Kotlin中抽象类和抽象函数的修饰符是abstract,声明抽象类Figure示例代码如下:
//代码文件:chapter13/src/com/a51work6/section1/Figure.kt
package com.a51work6.section1

abstract class Figure { ①
//绘制几何图形函数
abstract fun onDraw() //抽象函数 ②

abstract val name: String  //抽象属性         ③
val cname: String = "几何图形" //具体属性   ④

fun display() {//具体函数           ⑤
    println(name)
}

}
代码第①行是声明抽象类,在类前面加上abstract修饰符,这里不需要使用open修饰符,默认是open。代码第②行声明抽象函数,函数前面的修饰符也是abstract,也需要不需要使用open修饰符,默认也是open,抽象函数没有函数体。代码第③行的属性是抽象属性,所谓“抽象属性”是没有初始值,没有setter或getter访问器。代码第④行的属性是,所谓“具体属性”它有初始值或者有setter或getter访问器。代码⑤行是具体函数,它有函数体。
在这里插入图片描述
设计抽象类目的就是让子类来实现的,否则抽象就没有任何意义,实现抽象类示例代码如下:
//代码文件:chapter13/src/com/a51work6/section1/Ellipse.kt
package com.a51work6.section1

//几何图形椭圆形
class Ellipse : Figure() {
override val name: String ①
get() = “椭圆形”

//绘制几何图形函数
override fun onDraw() {             ②
    println("绘制椭圆形...")
}

}

//代码文件:chapter13/src/com/a51work6/section1/Triangle.kt
package com.a51work6.section1

//几何图形三角形
class Triangle(override val name: String) : Figure() { ③
// 绘制几何图形函数
override fun onDraw() { ④
println(“绘制三角形…”)
}
}
上述代码声明了两个具体类Ellipse和Triangle,它们实现(重写)了抽象类Figure的抽象函数onDraw,见代码第②行和第④行。代码第①行是Ellipse中实现name属性,在父类Figure中name属性是抽象的。代码第③行是实现在构造函数中提供了name属性,从而实现了name属性。比较代码第①行和第③行实现属性name方式有所不同,但是最终效果是一样的。
调用代码如下:
//代码文件:chapter13/src/com/a51work6/section1/ch13.1.kt
package com.a51work6.section1

fun main(args: Array) {
// f1变量是父类类型,指向实现类实例,发生多态
val f1: Figure = Triangle(“三角形”) ①
f1.onDraw()
f1.display() ②

// f2变量是父类类型,指向实现类实例,发生多态
val f2: Figure = Ellipse()
f2.onDraw()  
println(f2.cname)   ③

}
上述代码中实例化两个具体类Triangle和Ellipse,对象f1和f2是Figure引用类型。代码第①行是实例化Triangle对象,代码第②行是调用抽象类中的具体函数display()。代码第③行是调用抽象类中的具体属性cname。
在这里插入图片描述

13.2 使用接口

比抽象类更加抽象的是接口,接口中主要应该包含抽象函数和抽象属性,但是根据需要可以有具体函数和属性。
在这里插入图片描述
13.2.1 接口概念
其实13.1.1节抽象类Figure可以更加彻底,即Figure接口,虽然接口中可以有函数和属性,也有具体函数和属性,但接口不保存状态。将13.1.1节几何图形类改成接口后,类图如图13-2所示。
在这里插入图片描述
13.2.2 接口声明和实现
在Kotlin中接口的声明使用的关键字是interface,声明接口Figure示例代码如下:
//代码文件:chapter13/src/com/a51work6/section2/s2/Figure.kt
package com.a51work6.section2.s2

interface Figure { ①
//绘制几何图形函数
fun onDraw() //抽象函数 ②

val name: String  //抽象属性    ③

val cname: String //具体属性     ④
    get() = "几何图形"
    
fun display() {//具体函数           ⑤
    println(name)
}

}
代码第①行是声明Figure接口,声明接口使用interface关键字。代码第②行声明抽象函数,抽象函数没有函数体。代码第③行的属性是抽象属性,抽象属性是没有初始值,没有setter或getter访问器。代码第④行的具体属性,具体属性不能有初始值只能有getter访问器,说明该属性后面没有支持字段。代码⑤行是具体函数,它有函数体。
实现接口Figure示例代码如下:
//代码文件:chapter13/src/com/a51work6/section2/s2/Ellipse.kt
package com.a51work6.section2.s2

//几何图形椭圆形
class Ellipse : Figure {
override val name: String
get() = “椭圆形”

//绘制几何图形函数
override fun onDraw() {
    println("绘制椭圆形...")
}

}

//代码文件:chapter13/src/com/a51work6/section2/s2/Triangle.kt
package com.a51work6.section2.s2

//几何图形三角形
class Triangle(override val name: String) : Figure {
// 绘制几何图形函数
override fun onDraw() {
println(“绘制三角形…”)
}
}
上述代码声明了两个具体类Ellipse和Triangle,它们实现了接口Figure中的抽象函数onDraw和抽象属性name。
调用代码如下:
//代码文件:chapter13/src/com/a51work6/section2/s2/ch13.2.2.kt
package com.a51work6.section2.s2

fun main(args: Array) {
// f1变量是接口类型,指向实现类实例,发生多态
val f1: Figure = Triangle(“三角形”)
f1.onDraw()
f1.display()

// f2变量是接口类型,指向实现类实例,发生多态   

val f2: Figure = Ellipse()
f2.onDraw()
println(f2.cname)
}
上述代码中实例化两个具体类Triangle和Ellipse,对象f1和f2是Figure接口引用类型。代码与13.1.2抽象类调用,这里不再赘述。
在这里插入图片描述
13.2.3 接口与多继承
在C++语言中一个类可以继承多个父类,但这会有潜在的风险,如果两个父类有相同的函数,那么子类将继承哪一个父类函数呢?这就是C++多继承所导致的冲突问题。
在Kotlin中只允许继承一个类,但可实现多个接口。通过实现多个接口方式满足多继承的设计需求。如果多个接口中即便有相同抽象函数,子类实现它们不会有冲突。
图13-3所示是多继承类图,其中有两个接口InterfaceA和InterfaceB,从类图中可见两个接口中都有一个相同的函数methodB()。AB实现了这两个接口,继承了Any父类。
在这里插入图片描述
接口InterfaceA和InterfaceB代码如下:
//代码文件:chapter13/src/com/a51work6/section2/s3/InterfaceA.kt
package com.a51work6.section2.s3

interface InterfaceA {
fun methodA()
fun methodB()
}

//代码文件:chapter13/src/com/a51work6/section2/s3/InterfaceB.kt
package com.a51work6.section2.s3

interface InterfaceB {
fun methodB()
fun methodC()
}

从代码中可见两个接口都有两个抽象函数,其中函数methodB()定义完全相同。实现接口InterfaceA和InterfaceB的AB类代码如下:
//代码文件:chapter13/src/com/a51work6/section2/s3/AB.kt
package com.a51work6.section2.s3

class AB : Any(), InterfaceA, InterfaceB { ①
override fun methodC() {}
override fun methodA() {}
override fun methodB() {} ②
}

上述代码第①行是声明AB类,其中继承Any类,实现了两个接口。注意先声明继承父类,并指定调用父类的哪个构造函数,然后再是声明的接口,它们之间使用逗号(,)分隔。在AB类中的代码第②行实现methodB()函数,这个函数即实现了InterfaceA又实现了InterfaceB。

13.2.4 接口继承
Kotlin语言中允许接口和接口之间继承。由于接口中的函数都是抽象函数,所以继承之后也不需要做什么,因此接口之间的继承要比类之间的继承简单的多。如图13-4所示,其中InterfaceB继承了InterfaceA,在InterfaceB中还重写了InterfaceA中的methodB()函数。ABC是InterfaceB接口的实现类,从图13-4中可见ABC需要实现InterfaceA和InterfaceB接口中的所有函数。
在这里插入图片描述
接口InterfaceA和InterfaceB代码如下:
//代码文件:chapter13/src/com/a51work6/section2/s4/InterfaceA.kt
package com.a51work6.section2.s4

interface InterfaceA {
fun methodA()
fun methodB()
}
//代码文件:chapter13/src/com/a51work6/section2/s4/InterfaceB.kt
package com.a51work6.section2.s4

interface InterfaceB : InterfaceA {
override fun methodB()
fun methodC()
}

//代码文件:chapter13/src/com/a51work6/section2/s4/ABC.kt
package com.a51work6.section2.s4

class ABC : InterfaceB {
override fun methodA() {}
override fun methodB() {}
override fun methodC() {}
}

InterfaceB继承了InterfaceA,声明时也使用冒号(:)。InterfaceB 中的methodB()重写了InterfaceA,事实上在接口中重写抽象函数,并没有实际意义,因为它们都是抽象的,都是留给子类实现的。
实现接口InterfaceB的ABC类代码如下:
//代码文件:chapter13/src/com/a51work6/section2/s4/ABC.kt
package com.a51work6.section2.s4

class ABC : InterfaceB {
override fun methodA() {}
override fun methodB() {}
override fun methodC() {}
}
ABC类实现了接口InterfaceB,事实上是实现InterfaceA和InterfaceB中所有函数,相当于同时实现InterfaceA和InterfaceB接口。

13.2.5 接口中具体函数和属性
在Kotlin中接口主要成员是抽象函数和属性,但是也有具体函数和属性。接口中的抽象函数和属性是必须要实现的,而具体函数和属性是可选实现的,根据自己的业务需求选择是否重写它们。
接口中的具体属性和抽象属性在前面已经介绍过了,本节重点介绍在接口中使用具体函数。示例代码如下:
//代码文件:chapter13/src/com/a51work6/section2/s5/InterfaceA.kt
package com.a51work6.section2.s5

interface InterfaceA {

fun methodA()
fun methodB(): String

fun methodC(): Int {
    return 0
}

fun methodD(): String {
    return "这是默认函数..."
}

}

在接口InterfaceA中声明了两个抽象函数methodA和methodB,以及两个具体函数methodC和methodD,并给出了具体实现。
实现接口示例代码如下:
//代码文件:chapter13/src/com/a51work6/section2/s5/ABC.kt
package com.a51work6.section2.s5

class ABC : InterfaceA {

override fun methodA() {}

override fun methodB(): String {
    return "实现methodB函数..."
}

override fun methodC(): Int {
    return 500
}

}

实现接口时接口中原有的抽象函数在实现类中必须实现。抽象函数可以根据需要有选择重写。上述代码中ABC类实现了InterfaceA接口,InterfaceA接口中的两个抽象函数ABC只是重写了methodB。

调用代码如下:
//代码文件:chapter13/src/com/a51work6/section2/s5/ch13.2.5.kt
package com.a51work6.section2.s5

fun main(args: Array) {

//声明接口类型,实例是实现类,发生多态
val abc = ABC()

// 访问methodB函数
println(abc.methodB())

// 访问函数methodC
println(abc.methodC())      ①

// 访问函数methodD       ②
println(abc.methodD())

}
运行结果:
实现methodB函数…
500
这是默认函数…
从运行结果可见,代码第①行调用函数methodC,它是调用类AB中的实现。代码第②行调用函数methodD,是调用接口InterfaceA中的实现。

本章小结

通过对本章的学习,读者可以了解抽象类和接口的概念,掌握如何声明抽象类和接口,如何实现抽象类和接口。熟悉抽象类和接口的区别。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
回答: 要从小到大,学习Kotlin需要掌握一些基本的知识和技能。首先,你可以使用交互式方式运行Kotlin代码,这可以通过使用REPL(Read-Eval-Print Loop)实现。REPL允许你逐行输入和执行Kotlin代码,这对于学习和测试代码非常有用。你可以使用Kotlin编译器提供的kotlin命令来启动REPL。\[3\] 另外,你还可以使用文本编辑工具编写Kotlin源文件,然后使用Kotlin编译器提供的kotlinc命令在命令提示行中编译Kotlin源程序。编译成功后,你可以使用kotlin命令或JDK提供的java命令来运行编译后的Kotlin程序。\[3\] 如果你想使用IntelliJ IDEA或Eclipse等集成开发环境来开发Kotlin项目,你可以创建一个新的Kotlin项目,并在项目中创建文件,编写代码,然后通过IDE提供的编译和运行功能来执行你的程序。这种方式更适合实际项目的开发。\[2\] 总之,要从小到大,你需要不断学习和实践Kotlin编程,掌握基本的语法和概念,并熟悉常用的开发工具和技巧。通过不断的练习和项目实践,你可以逐渐提升自己的Kotlin编程能力,成为一名Kotlin。 #### 引用[.reference_title] - *1* *2* *3* [《Kotlin从小到大》第3章:第一个Kotlin程序](https://blog.csdn.net/weixin_38072116/article/details/106554349)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值