7.3 抽象类
7.3.1 抽象类的定义
含有抽象函数的类(这样的类需要使用abstract修饰符来声明),称为抽象类。
下面是一个抽象类的例子:
1. abstract class Person(var name: String, var age: Int) : Any() {
3. abstract var addr: String
4. abstract val weight: Float
6. abstract fun doEat()
7. abstract fun doWalk()
9. fun doSwim() {
10. println("I am Swimming ... ")
11. }
13. open fun doSleep() {
14. println("I am Sleeping ... ")
15. }
16. }
7.3.2 抽象函数
在上面的这个抽象类中,不仅可以有抽象函数abstract fun doEat() abstract fun doWalk(),同时可以有具体实现的函数fun doSwim(), 这个函数默认是final的。也就是说,我们不能重写这个doSwim函数:

如果一个函数想要设计成能被重写,例如fun doSleep(),我们给它加上open关键字即可。然后,我们就可以在子类中重写这个open fun doSleep():
1. class Teacher(name: String, age: Int) : Person(name, age) {
2. override var addr: String = "HangZhou"
3. override val weight: Float = 100.0f
5. override fun doEat() {
6. println("Teacher is Eating ... ")
7. }
9. override fun doWalk() {
10. println("Teacher is Walking ... ")
11. }
13. override fun doSleep() {
14. super.doSleep()
15. println("Teacher is Sleeping ... ")
16. }
18. // override fun doSwim() { // cannot be overriden
19. // println("Teacher is Swimming ... ")
20. // }
21. }
抽象函数是一种特殊的函数:它只有声明,而没有具体的实现。抽象函数的声明格式为:
1. abstract fun doEat()
关于抽象函数的特征,我们简单总结如下:
- 抽象函数必须用abstract关键字进行修饰
- 抽象函数不用手动添加open,默认被open修饰
- 抽象函数没有具体的实现
- 含有抽象函数的类成为抽象类,必须由abtract关键字修饰。抽象类中可以有具体实现的函数,这样的函数默认是final(不能被覆盖重写),如果想要重写这个函数,给这个函数加上open关键字。
### 7.3.3 抽象属性
抽象属性就是在var或val前被abstract修饰,抽象属性的声明格式为:
1. abstract var addr : String
2. abstract val weight : Float
关于抽象属性,需要注意的是:
- 抽象属相在抽象类中不能被初始化
- 如果在子类中没有主构造函数,要对抽象属性手动初始化。如果子类中有主构造函数,抽象属性可以在主构造函数中声明。
综上所述,抽象类和普通类的区别有:
1.抽象函数必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public。
也就是说,这三个函数
1. abstract fun doEat()
2. abstract fun doWalk()
4. fun doSwim() {
5. println("I am Swimming ... ")
6. }
默认的都是public的。
另外抽象类中的具体实现的函数,默认是final的。上面的三个函数,等价的Java的代码如下:
1. public abstract void doEat();
3. public abstract void doWalk();
5. public final void doSwim() {
6. String var1 = "I am Swimming ... ";
7. System.out.println(var1);
8. }
2.抽象类不能用来创建对象实例。也就是说,下面的写法编译器是不允许的:

3.如果一个类继承于一个抽象类,则子类必须实现父类的抽象方法。实现父类抽象函数,我们使用override关键字来表明是重写函数:
1. class Programmer(override var addr: String, override val weight: Float, name: String, age: Int) : Person(name, age) {
2. override fun doEat() {
3. println("Programmer is Eating ... ")
4. }
6. override fun doWalk() {
7. println("Programmer is Walking ... ")
8. }
9. }
如果子类没有实现父类的抽象函数,则必须将子类也定义为为abstract类。例如:
1. abstract class Writer(override var addr: String, override val weight: Float, name: String, age: Int) : Person(name, age) {
2. override fun doEat() {
3. println("Programmer is Eating ... ")
4. }
6. abstract override fun doWalk();
7. }
doWalk函数没有实现父类的抽象函数,那么我们在子类中把它依然定义为抽象函数。相应地这个子类,也成为了抽象子类,需要使用abstract关键字来声明。
如果抽象类中含有抽象属性,在实现子类中必须将抽象属性初始化,除非子类也为抽象类。例如我们声明一个Teacher类继承Person类:
1. class Teacher(name: String, age: Int) : Person(name, age) {
2. override var addr: String // error, 需要初始化,或者声明为abstract
3. override val weight: Float // error, 需要初始化,或者声明为abstract
4. ...
5. }
这样写,编译器会直接报错:

解决方法是,在实现的子类中,我们将抽象属性初始化即可:
1. class Teacher(name: String, age: Int) : Person(name, age) {
2. override var addr: String = "HangZhou"
3. override val weight: Float = 100.0f
5. override fun doEat() {
6. println("Teacher is Eating ... ")
7. }
9. override fun doWalk() {
10. println("Teacher is Walking ... ")
11. }
12. }
