1. 讲啥的
关于kotlin的构造函数,相信很多小白也和我一样,感觉很混乱,因为它和java不一样,既有主构造函数又有次构造函数,构造函数的参数里也可以加修饰符,将参数作为全局变量,还有init代码块等。所以我觉得有必要梳理一篇文章,整理下这部分知识,毕竟,这些知识在具体的业务开发中,也是常用的。
看完这篇文章,你将了解到的有
- kotlin的构造函数种类
- kotlin的构造函数中有val/var和无val/var的区别
- 构造函数和init代码块的区别
- 构造函数和伴生对象里的init代码块的区别
这里的所谓区别,多数是调用顺序的区别,即表面上的区别,至于底层的区别,暂时不做探究
2. 正文
2.1 kotlin的构造函数种类
从总体上分,其实就是两种:主构造函数和次构造函数。那什么是主构造函数,什么是次构造函数呢?我觉得从写法上就可以区分,看下面的例子
(1)先来看主构造函数
class Person constructor(name: String, age: Int) {
}
或者省略constructor,这样写
class Person(name: String, age: Int) {
}
从这也可以看出:主构造函数可以省略constructor
(2)再来看次构造函数
class Person() {
constructor(score: Int): this() {
}
constructor(name: String, age: Int): this() {
}
}
从这也可以看出,次构造函数可以有多个,而且如果主构造函数有显式声明,则次构造函数必须直接或者间接地调用主构造函数。
为什么这么说呢?如果主构造函数没有显式地声明,则次构造函数不用显式地调用,像这样
class Person {
constructor(score: Int) {
}
constructor(name: String, age: Int) {
}
}
2.2 kotlin的主构造函数
上面的内容,从整体上看了下kotlin的主构造函数与次构造函数。下面,来较为详细地看一下kotlin的主构造函数的特点。
(1)主构造函数的参数,被val/var修饰有什么用?
首先,来解决文章开篇提到的问题,就是kotlin的构造函数的参数中,加val/var与不加的区别?其实这里的构造函数,指的是主构造函数。因为次构造函数的参数是不允许加val/var的,如图
那主构造函数,参数加val/var的作用是什么呢?加了val/var的变量,将作为此类全局的成员变量。举个例子
class Person(val name: String, age: Int) {
fun doSomething() {
println(name) // name是可以引用到的
// println(age) // age是引用不到的
}
}
在这个例子中,name加了val修饰,可以在类的成员方法中引用到,比如doSomething这个方法。而age没有加val/var,则在doSomething这个方法中无法引用到
(2)init与主构造函数的联系是什么?
先来看这样一段代码
class Person(name: String, age: Int) {
init {
println("init 中, name:$name , age:$age ")
}
constructor(name: String, age: Int, score: Int): this(name, age) {
println("次构造函数 中, name:$name , age:$age , score:$score")
}
}
然后我在main函数中,创建一个Person对象
fun main() {
var person = Person("张三", 21, 100)
}
打印结果如下
init 中, name:张三 , age:21
次构造函数 中, name:张三 , age:21 , score:100
从这个打印结果中,我觉得可以至少发现两个问题
- init代码块比次构造函数先执行
- init代码块,可以理解为主构造函数的方法体
其中第一个结论好理解,第二个结论我解释一下。刚刚我们做过实验:假如主构造函数的参数中,没有加val/var,那在doSomething这个成员方法里面是引用不到这个变量的。而在init里面,居然引用到了,说明init和主构造函数,是在一个作用域里面的,所以,我觉得可以理解为主构造函数的方法体。
那么这个结论到底靠不靠谱呢?让我们将kotlin转为java,验证一波
可以看到,init代码块中的内容,确实在主构造函数的方法体里面了,验证完毕。
(3)伴生对象里面的init,和外部的构造函数与init,执行顺序又是怎么样的?
看这个问题描述貌似有点乱,我先来一段代码,大家一目了然
class Person(name: String) {
init {
println("init 中, name:$name")
}
companion object {
init {
println("companion object 中, init代码块")
}
}
constructor(name: String, score: Int): this(name) {
println("次构造函数 中, name:$name , score:$score")
}
}
大家不妨先猜一猜,这个执行顺序是怎么样的。下面,我来揭晓答案
companion object 中, init代码块
init 中, name:张三
次构造函数 中, name:张三 , score:100
结果是,伴生对象里面的init代码块先执行。结合伴生对象的特点,我们不妨大胆猜测:
伴生对象里面的init代码块,是类似java的static代码块,随着类一起加载的,而外部的主构造函数,次构造函数,说白了都是构造函数,只有创建对象的时候才会调用。同样的,我们还是验证一波
可以看到,确实是这样。
2.3 kotlin的次构造函数
次构造函数,用的相对比较少,而且使用起来不算复杂,其简单的调用规则,上文其实已经说的很清楚了,这里就不再重复了。
3. 总结
梳理完这一篇文章,我对kotlin的构造函数,理解又全面加深了一层,最起码不像以前一样,一头雾水,代码能不能跑全靠试了。不知道你们是否和我一样。文章开篇提出的几个问题,也都在文中做了解答,大家可以在文中寻找答案。另外,如有错误,还请各位批评指正!
加油!