有趣的 Kotlin 0x05:Making open abstract

1,480 阅读3分钟

最近在 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()

以上代码,运行结果是什么?可选项:

  1. Compiles fine
  2. Error: Class 'C' is not abstract and does not implement abstract base class member
  3. Error: 'a' overrides nothing
  4. Error: Function 'a' must have a body

思考一下,记录下你心中的答案。

分析

Kotlin 中 open 关键字,用于类上,表示该类可以被继承,用于函数上,表示该函数可以被重写。 Java 中却无需如此,在 Java 中默认允许创建任意的子类并重写任意的方法,除非明确使用 final 关键字进行标注表示类不可继承、函数不可重写。而 Kotlin 中类默认是 final 不可继承的,类中的方法默认也是 final 不可重写的,于是 open 关键字应运而生。

Kotlin 中 abstract 关键字可以修饰类、函数和属性,分别表示抽象类、抽象函数和抽象属性。抽象函数在抽象类中不允许实现,抽象属性在抽象类中也不允许初始化。我们也不需要用 open 关键字来修饰抽象类、抽象函数以及抽象属性来告诉编译器它们可以被继承或者被重写,因为抽象的意义就在于被实现。

abstract and open

所以,题目中 AB 两个类的写法是正确的。重点在于 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()

总结

  1. Kotlin 中类默认是 final 不可继承的,函数和属性亦是如此;
  2. 父类是抽象类,若子类是非抽象类,则必须实现父类中所有的抽象方法和抽象属性;若子类为抽象类,则没有这个必要;
  3. 抽象类、抽象函数和抽象类无需再用 open 关键字修饰;
  4. open 函数可以被抽象函数重写。