Kotlin从入门到放弃 第五节 继承与构造函数

这章学习一下一个很重要的特性——继承。

首先创建一个student类在里面加入学号和年纪两个字段:

class Student {

var sno = ""

var grade = 0

}

现在Student和Person这俩个类之间是没有任何继承关系的,想要让Student类继承Person类,还需要做两件事。

第一件事,让Person类可以被继承。这里要说一下在Kotlin中任何一个非抽象类默认都是不可以被继承的,相当于Java中给类声明了final。那么想要让Person类变成可以被继承的方法也很简单。在Person类的前面加上open关键字就可以了:

open class Person{

...

}

加上open关键字之后,就是在告诉Kotlin编译器,Person这个类是专门为继承而设计的,这样Person这个类就可以被继承了。

接下来要让Student继承Person类。在Java中继承的关键字是extends,而在Kotlin中变成了一个冒号。

class Student : Person() {

var sno = ""

var grade = 0

}

继承的写法如果只是替换一个关键那么还是非常简单的,但是为什么Person后面要加上一个括号呢?Java中继承的时候好像不需要加括号。对于初学Kotlin的人来说,这个括号确实挺难理解的。也可能是Kotlin在这方面设计的有些复杂,因为它还涉及到了主构造函数、次构造函数等方面的知识,这里我们经量尝试用最简单易懂的讲述来让你理解这对括号的意义和作用,同时顺便学习一下Kotlin中的主次构造函数。

任何一个面向对象的编程语言都会有构造函数的概念,Kotlin中也有,但是Kotlin将构造函数分成了两种:主构造函数和次构造函数。

主构造函数将会是你最常用的构造函数,每个类都会默认有一个不带参数的主构造函数,当然你也可以使用显示的给他指明参数。主构造函数的特点是没有函数体,直接定义在类名后面即可。

class Student(val sno: String, val grade: Int) {

}

这里我们将学号和年纪这两个字段都放到了主构造函数中,这就表明在对Student类进行实例化的时候,必须传入构造函数中要求的参数。

val student = Student("001", 3)

上面的代码就是创建了一个Student对象,同时指定了这名学生的学号是001,年纪是3年纪。由于构造函数中的参数是在创建实例化的时候传入的,不想之前的写法那样还要重新赋值,因此我们就可以把参数全都声明成val。

这里可能会有一个疑问,主构造函数没有函数体,如果我想在主构造函数中编写一些逻辑,该怎么办呢?Kotlin给我们提供了一个init结构体,所有的主构造函数中的逻辑都可以写在里面:

class Student(val sno: String, val grade: Int) : Person() {

init{

Log.i("sun is " + sno)

Log.i("grade is " + grade)

}

}

这里只是简单的打印了一下基本信息,现在如果你再去创建一个Student类的对象,一定会将构造函数中传入的数据打印出来。

到这里为止,其实都很好理解,但是这个那对括号又有什么关系呢?这就涉及了Java继承特性中的一个规定,子类中的构造函数必须调用父类中的构造函数,这个规定在Kotlin中也要遵守。

那么我们再来看下Student类,现在我们声明了一个主构造函数,根据继承特性的规定,子类的构造函数必须调用父类的构造函数,可以主构造函数并没有函数体,我们怎么调用父类的构造函数呢? 你可能会说,在init中调用不就可以了吗?或与这是一个办法,但绝对不是一种好办法,因为在绝大多数的场景下,我们是不需要init结构体的。

Kotlin当然没有采用这种设计,而是用了另外一种简单但是可能不太好理解的设计方式:括号。子类的主构造函数调用父类的那个构造函数,在继承的时候通过括号类指定。因此再来看一下这个代码,你应该就可以理解了:

class Student(val sno: String, val grade: Int) : Person() {

}

在这里,Person类后面的空括号表时调用了Person类中的空构造函数,即使是在没有参数的情况下,括号也不能省略。

但是如果我们将Person类改一下:

open class Person(val name: String, val age: Int) {

}

此时你的Student类会报错。 报错原因很明显,空括号表时要调用空构造,但是现在Person中已经没有空构造了,修正方式也很简单:

class Student(val sno: String, val grade: Int, name: String, age: Int): Person(name, age) {

}

值得注意的是Student中的name和age不能在将他们声明val,因为在主构造函数中声明val或var的参数将自动成为该类的字段,这就会导致和父类中的name和age重名。因此,这里的name和age参数前面我们不用加任何关键字。

OK,继续说一下我的次构造函数。

其实我们几乎是用不到次构造函数的,Kotlin提供了一个给函数设定参数默认值的功能,基本上可以替代次构造函数的作用。

你要知道,任何一个类只能有一个主构造函数,但是可以又多个次构造函数。次构造函数也可以用于实例化一个类,这一点和主构造函数没什么不同,只不过它是又函数体的。

Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)。举个简单的例子:

class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) {

constructor(name: String, age: Int) : this ("", 0, name, age){

}

constructor(): this("", 0) {

}

}

次构造函数时通过constructor关键字定义,这里我们定义了两个次构造函数:第一个次构造函数接收name和age参数,然后又通过this关键字调用了主构造函数,并将sno和grade这两个参数赋值成初始值;第二个次构造函数不接受任何参数,它通过this关键字调用了我们刚才定义的第一个次构造函数,并将name和age参数也赋值成初始值,由于第二个次构造函数间接的调用了主构造函数,因此也是合法的。

那么我现在就拥有了3种方式来对Student进行实例化,分别是通过不带参数的构造函数和两个带参数的构造函数和通过带4个参数的构造函数:

val student1 = Student()

val student2 = Student("name", 0)

val student3 = Student("aaa", 0, "name", 19)

这样我们就将次构造函数的用法掌握的差不多了,但是到目前为止,继承时的括号问题还没有进一步延伸,暂时和之前学过的场景是一样的。

那么接下来再来看一种非常特殊的情况:类种只有一个次构造函数,没有主构造函数。这种情况非常少见,但是在Kotlin中时允许的。当一个类没有显示的定义主构造函数且定义了次构造函数时,它就是没有主构造函数的,看下代码:

class Student : Person {

constructor(name: String, age: Int) : super(name, age) {

}

}

注意这里的代码变化,首先Student类的后面没有显示的定义主构造函数,同时又因为定义了次构造函数,所以现在Student类是没有主构造函数的。那么既然没有主构造函数,继承Person类的时候也就不需要加上括号了。其实原因就是这么简单,只是有人在刚开始学习Kotlin的时候没有能理解这个括号的意义的规则,因此总感觉继承的写法有时候要加括号,有时候又不加括号,搞得晕头转向的,而在真正理解了规则之后,就会发现其实还是很好懂的。

另外,由于没有主构造函数,次构造函数只能直接调用父类的构造函数,上面的代码也是将this换成了super,这里就很简单了,因为和Java比较像,就不多说了。

OK这些就是Kotlin的继承和构造函数问题,觉得有用小伙伴帮忙点下赞呦!!!

文章链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: