- 接口声明和实现
- 接口与多继承
- 接口继承
- 接口中的具体函数和属性
- ✔️同一函数继承多个实现的问题
- 比抽象类更加抽象的是接口,接口包括 抽象函数和抽象属性 ,也可以根据需要有 具体的函数 和 具体属性(没有‘field’)。
注意:接口和抽象类都可以有抽象函数和抽象属性,也可以有具体函数和具体属性。那么接口和抽象类有什么区别?接口不能维护一个对象状态,而抽象类可以,因为维护一个对象状态需要支持字段,而接口中无论是具体属性还是抽象属性,后面都 没有支持字段。
// 抽象类
abstract class Figure {
abstract fun onDraw() // 抽象函数,无函数体
abstract val name: String // 抽象属性,无初始值,无getter和setter访问器
var cname: String = "几何图形" // 具体属性 1️⃣
get() = field
set(value) {
field = "Figure -> $value"
}
fun display() = println(name) // 具体函数
}
// 接口
interface Figure {
fun onDraw() // 抽象函数,无函数体
val name: String // 抽象属性,无初始值,无getter和setter访问器
var cname: String // 3️⃣具体属性
get() = "几何图形"
set(value) {
// 逻辑体
}
//var cname: String = "几何图形" // 2️⃣编译错误,接口不具有支持字段 field
// get() = field
// set(value) {
// field = "Figure -> $value"
// }
fun display() = println(name) // 具体函数
}
代码第1️⃣行是在抽象类中声明了一个具体属性 cname,且可以正常编译通过;
代码第2️⃣行是在接口中声明了一个具体属性 cname,编译不通过,因为没有 支持字段(field) 存储状态。
接口中的声明具体属性,不能有初始值,并且 getter 和 setter 访问器中不能使用支持字段 field。例如上面代码第3️⃣行。
一、接口声明和实现
在 kotlin 中接口声明使用的关键字是 interface。接口中可以定义 0 - N 个抽象函数和属性,也可以定义 0 - N 个具体函数和属性。
interface 接口名 {
fun 函数名(参数列表): 返回值类型 // 抽象函数,可有可无
var|val 属性名: 属性类型 // 抽象属性,可有可无
fun 函数名(参数列表): 返回值类型 { // 具体函数,可有可无
// 函数体
}
val 属性名: 属性类型 // 具体属性,可有可无
get() {
// 不能访问 feild
return 具体类型实例
}
set(value) {
// 逻辑体
// 不能访问 feild
}
}
例如声明一个 Figure
接口的示例:
interface Figure { // 4️⃣
fun onDraw() // 5️⃣抽象函数,无函数体
val name: String // 6️⃣抽象属性,无初始值,无getter和setter访问器
val cname: String // 7️⃣具体属性
get() = "几何图形"
fun display() = println(name) // 8️⃣具体函数
}
代码第4️⃣行是声明
Figure
接口,声明接口使用 interface 关键字。代码第5️⃣行是声明抽象函数,抽象函数没有函数体。
代码第6️⃣行是声明抽象属性,抽象属性没有初始值,没有 getter 和 setter 访问器。
代码第7️⃣行的属性,是具体属性,具体属性不能有初始值,并且 getter 和 setter 访问器中不能使用支持字段 field。
代码第8️⃣行的函数,是具体函数,它有函数体。
提示:具体函数和属性,可以理解为抽象函数和属性的默认实现。
class Ellipse : Figure {
override val name: String
get() = "椭圆形"
override fun onDraw() {
println("绘制椭圆形...")
}
override fun display() {
println("Ellipse -> $name")
}
}
class Triangle(override val name: String) : Figure {
override var cname: String
get() = "Triangle -> $name"
set(value) { }
override fun onDraw() {
println("绘制三角形...")
}
}
class Rectangle : Figure {
override val name: String = "矩形"
override fun onDraw() {
println("绘制矩形...")
}
}
fun main(args: Array<String>) {
val f1 = Ellipse()
f1.onDraw()
f1.display()
println(f1.cname)
val f2 = Triangle("三角形")
f2.onDraw()
f2.display()
println(f2.cname)
}
2019-06-03 17:03:49.315 15906-15906/cn.ak.kot I/System.out: 绘制椭圆形...
2019-06-03 17:03:49.315 15906-15906/cn.ak.kot I/System.out: Ellipse -> 椭圆形
2019-06-03 17:03:49.316 15906-15906/cn.ak.kot I/System.out: 几何图形
2019-06-03 17:03:49.318 15906-15906/cn.ak.kot I/System.out: 绘制三角形...
2019-06-03 17:03:49.318 15906-15906/cn.ak.kot I/System.out: 三角形
2019-06-03 17:03:49.318 15906-15906/cn.ak.kot I/System.out: Triangle -> 三角形
注意:接口和抽象类一样,都不能被实例化。
二、接口与多继承
在 kotlin 中只允许继承一个类,但可以实现多个接口。通过实现多个接口的方法满足多继承的设计需求。
interface InterfaceA {
fun methodA()
fun methodB()
}
interface InterfaceB {
fun methodB()
fun methodC()
}
class AB: Any(), InterfaceA, InterfaceB {
override fun methodA() {
println("methodA()")
}
override fun methodB() { // 9️⃣
println("methodB()")
}
override fun methodC() {
println("methodC()")
}
}
- 代码第9️⃣行是即实现了接口 InterfaceA 的抽象方法,也实现了接口 InterfaceB 的抽象方法。
三、接口继承
- kotlin 中允许接口和接口之间继承。由于接口中的函数都是抽象函数,没有函数体,所以继承之后也不需要额外做什么。
- 接口继承时使用 (:) 声明,当继承多个接口时使用 (,) 分隔。
interface InterfaceA {
fun methodA()
}
interface InterfaceB {
fun methodB()
}
interface InterfaceC : InterfaceA, InterfaceB {
override fun methodB() // 🔟
fun methodC()
}
class ABC : InterfaceC {
override fun methodA() {
println("methodA()")
}
override fun methodB() {
println("methodB()")
}
override fun methodC() {
println("methodC()")
}
}
代码第🔟行,接口 InterfaceC
中的 methodB()
重写了接口 InterfaceB
,事实上在接口中重写抽象函数并没有实际意义,因为它们都是抽象的,都要留给子类实现的。
四、接口中的具体函数和属性
在 kotlin 中接口的主要成员是抽象函数和抽象属性,但是也有具体函数和具体属性。接口中的抽象函数和抽象属性是必须要实现的,而具体函数和具体属性是可选择实现的。
// 定义接口InterfaceA
interface InterfaceA {
fun methodA()
fun methodB(): Int
fun methodC(): String {
return "InterfaceA -> methodC"
}
fun methodD(): Int = 0
}
// InterfaceA实现类
class InterfaceAImpl : InterfaceA {
override fun methodA() { }
override fun methodB(): Int {
return 0
}
override fun methodC(): String {
return "InterfaceAImpl -> methodC"
}
}
InterfaceAImpl
必须实现接口 InterfaceA
中的抽象方法 methodA()
和 methodB()
,同时选择性的重写了接口 InterfaceA
中的具体函数 methodC()
。
五、✔️同一函数继承多个实现的问题
实现多个接口时,可能会遇到同一函数继承多个实现的问题。例如:
interface A {
var name: String
var age: Int
fun foo() {
println("A")
}
fun bar()
}
interface B {
val age: Int
get() = 15
// val age: String // 这种情况,实现类C,是无法同时继承接口A和B
// get() = "15"
fun foo() {
println("B")
}
fun bar() {
println("bar")
}
}
class C : A, B {
override var age: Int = 0
override var name: String = "C"
override fun foo() {
super<A>.foo()
super<B>.foo()
}
override fun bar() {
super.bar()
}
}
fun main(args: Array<String>) {
val c = C()
println("name = ${c.name}")
println("age = ${c.age}")
c.foo();
c.bar();
}
2019-06-03 18:32:34.377 24711-24711/cn.ak.kot I/System.out: name = C
2019-06-03 18:32:34.378 24711-24711/cn.ak.kot I/System.out: age = 0
2019-06-03 18:32:34.378 24711-24711/cn.ak.kot I/System.out: A
2019-06-03 18:32:34.378 24711-24711/cn.ak.kot I/System.out: B
2019-06-03 18:32:34.378 24711-24711/cn.ak.kot I/System.out: bar
当遇到同一函数继承多个实现时,子类必须重写该函数,指定具体调用哪方实现,调用方式super<类名>.函数名()
,或都不调用实现子类自己的函数体。属性也是如此,但如果遇到属性名一致但类型不一致时,实现类是无法同时继承双方接口的,如上面被注释代码val age: String
。
interface A {
fun foo()
}
interface B {
fun foo(): String
}
class C : A, B { // 编译错误,无法同时继承接口A和B
override fun foo(): String {
return ""
}
override fun foo() {}
}
class D : A, B { // 编译错误,无法同时继承接口A和B
override fun foo(): String {
return ""
}
}
这种情况,接口中抽象函数名一致,返回类型不同,也会导致无法同时继承不同接口。