Kotlin的构造函数分为两种:主构造函数
和次构造函数
。
主构造函数
最常用的函数,每个类class
都默认包含一个主构造函数
,你也可以显式地给它指明参数。主构造函数
的特点是没有fun
这样的函数体,直接写在我们类的名称后面。两种写法如下:
class Student1: Human() {
var number = ""
var grade = 0
}
class Student2(val number: Number, val grade: String){
}
第二种写法:两参数为实例化时必须传入的参数,你在具体到哪个学生时必须同时提供学号
和年级
。
所以,我们不用之后再赋值,可以定义为val
,实例化代码如下:
fun main(){
val xiaoming = Student2(number = "0161120111", grade = 4)
xiaoming.name = "小明"
xiaoming.age = 22
xiaoming.eat()
}
那么问题来了,我们单用主构造函数,如何完成一些逻辑,例如:吃东西。
这里我们使用init()
就是一般用作初始化的那个函数,这个函数是默认运行的,不用调用。代码如下:
class Student2(val number: String, val grade: Number): Human(){
init {
println("学号是$number 的$grade 学生$name 正在吃饭,他已经$age 岁了。")
}
}
我们看一下结果:
可以很明显看到:第一句打印,并没有姓名
和年龄
。是在我们给name
与age
赋值之前,就已经运行了。
Kotlin在这里规定:若我们继承,子类必须调用父类的构造函数。
就像孩子一定要继承爸爸的某些特征,不可能有例外。
而我们继承时,父类后面的()
就是指定了我们要继承父类的哪个构造函数。
这里你是造物主,你可以指定孩子要继承父亲的哪些特征。虽然我们什么都没有写,可父类默认包含一个主构造函数、子类也默认调用了这个主构造函数,只是它没有参数没有内容而已。
下面我们把父类加入有参的主构造函数,转至Human.kt
,更改代码如下:
open class Human(name: String, age: Number) {
init {
println("$age 的$name 正在吃饭。")
}
}
随后我们回到Student.kt
,更改代码如下:
class Student(val number: String, val grade: Number,name: String, age: Number):
Human(name,age){
init {
println("学号是$number 的$grade 学生$name 正在吃饭,他已经$age 岁了。")
}
}
我们发现,Student
里面我们定义了name
,age
,后面Human()
里还有name,age
。
这里要注意,我们现在定义的Student
类,继承Human
父类时需要传入name,age
两个参数。
所以我们需要在构建Student
时定义name
和age
,在继承时才能传给Human
值。
当然与父类定义重名时会冲突,所以这里没有val
或var
。
下面我们来看示例代码:
package com.example.helloword
class Student(val number: String, val grade: Number,name: String, age: Number):
Human(name,age){
init {
println("学号是$number 的$grade 学生$name 正在吃饭,他已经$age 岁了。")
}
}
fun main(){
val xiaoming = Student(number = "0161120111", grade = 4,name = "小明", age = 22)
}
运行结果如下:
次构造函数
其实我们实践中几乎用不到次构造函数,这里只做了解进行讲解。对于次构造函数,你需要了解以下内容:
- 任何一个类只有一个
主构造函数
,可以有多个次构造函数
。 次构造函数
可以用于实例化
一个类,此点与主构造函数
相同。次构造函数
必须调用主构造函数
。- 当一个类没有显式定义一个
主构造函数
,而显式定义了一个次构造函数
,此类没有主构造函数
。 - 次构造函数通过
constructor
定义
次构造函数
像一个小跟班一样,跟着主构造函数
完成相应的功能。
下面我们看代码:
package com.example.helloword
class Student(val number: String, val grade: Number,name: String, age: Number):
Human(name,age){
init {
println("学号是$number 的$grade 年级学生$name 正在吃饭,他已经$age 岁了。")
}
constructor(name: String, age: Number):this("111111",0,name, age){
}
constructor():this("小明2", 22){
}
}
- 第一个
次构造函数
,传入name
和age
,并用this
调用了了上面的主构造函数,number
和grade
为写好的传入值,name
和age
是我们实例化时的传入值。 - 第二个
次构造函数
,没有传入值,用this
调用上面的第一个次构造函数
(间接调用主构造函数
),this
后直接指定了name
和age
,因为第一个次构造函数传入值为name
和age
,而number
和grade
则沿用第一个次构造函数
的定义值。
fun main(){
val student1 = Student()
println("")
val student2 = Student("小明3", 23)
println("")
val student3 = Student("222222", 4,"小明4", 24)
}
在主函数中,我们就有了三种实例化的格式,分别对应:
- 第二个
次构造函数
- 第一个
次构造函数
主构造函数
来看运行结果:
特殊情况
最后还有一种无主构造函数,有次构造函数情况。代码如下:
class Student:Human{
constructor(name:String, age:Number):super(name, age){
}
}
注意:
Student
后面没有主构造函数()
,因此Human
后也不需要加()
次构造函数
直接调用父类构造函数
- 所以
Human
后的括号不是必须的,需要看主构造函数
。
关于super
和this
在构造函数中使用方法:
super(参数)
:调用父类中的某一个构造函数。this(参数)
:调用本类中另一种形式的构造函数。
构造函数是不是有点点难呢,大家好好消化一下。
欢迎关注小试编程