最近在 portal.kotlin-academy.com/#/ 上看到很多关于 Kotlin 的有趣的题目。个人觉得很适合 Kotlin 爱好者,感兴趣的小伙伴可自行查阅。
【有趣的 Kotlin 】系列记录自己对每一题的理解。
0x05:Making open abstract
open class A {
open fun a() {}
}
abstract class B: A() {
abstract override fun a()
}
open class C: B()
复制代码
以上代码,运行结果是什么?可选项:
- Compiles fine
- Error: Class ‘C’ is not abstract and does not implement abstract base class member
- Error: ‘a’ overrides nothing
- Error: Function ‘a’ must have a body
思考一下,记录下你心中的答案。
分析
Kotlin 中 open
关键字,用于类上,表示该类可以被继承,用于函数上,表示该函数可以被重写。 Java
中却无需如此,在 Java 中默认允许创建任意的子类并重写任意的方法,除非明确使用 final
关键字进行标注表示类不可继承、函数不可重写。而 Kotlin 中类默认是 final
不可继承的,类中的方法默认也是 final
不可重写的,于是 open
关键字应运而生。
Kotlin 中 abstract
关键字可以修饰类、函数和属性,分别表示抽象类、抽象函数和抽象属性。抽象函数在抽象类中不允许实现,抽象属性在抽象类中也不允许初始化。我们也不需要用 open
关键字来修饰抽象类、抽象函数以及抽象属性来告诉编译器它们可以被继承或者被重写,因为抽象的意义就在于被实现。
所以,题目中 A
和 B
两个类的写法是正确的。重点在于 C
类的写法:
abstract class B: A() {
abstract override fun a()
}
open class C: B()
复制代码
C
类继承抽象类 B
,且 C
类不是抽象类,所以 C
类必须实现 B
中的抽象函数 a()
,但是题中未实现,所以按照我们的分析,题中代码会编译报错,且报错原因为类 C
未实现 B
类中的抽象方法 a()
。因此,正确答案为:
选项2:Error: Class ‘C’ is not abstract and does not implement abstract base class member
知道原因,调整代码让其正确编译的方式也就很明显了,要么修改类 B
,干掉抽象函数或者重写函数 a()
,要么修改类 C
,实现抽象函数或者将其改为抽象类。
方法一:
open class A {
open fun a() {}
}
abstract class B : A() {
}
open class C : B()
复制代码
方法二:
open class A {
open fun a() {}
}
abstract class B: A() {
abstract override fun a()
}
open class C: B() {
override fun a() {
println("a impl")
}
}
复制代码
方案三:
open class A {
open fun a() {}
}
abstract class B: A() {
abstract override fun a()
}
abstract class C: B()
复制代码
总结
- Kotlin 中类默认是
final
不可继承的,函数和属性亦是如此; - 父类是抽象类,若子类是非抽象类,则必须实现父类中所有的抽象方法和抽象属性;若子类为抽象类,则没有这个必要;
- 抽象类、抽象函数和抽象类无需再用
open
关键字修饰; open
函数可以被抽象函数重写。