Kotlin的构造函数分为主构造函数(primary constructor)和次级构造函数(secondary constructor);
主构造函数
1、在Kotlin中一个类可以有一个主构造函数和一个或多个次构造函数;如果不写构造函数会有一个默认空的构造函数
// 关键字 类名 类头(参数、主构造函数){ 类体 }
class MyTest{
}
var test =MyTest() //使用默认的构造函数创建对象
// 空的类
class MyTestEmpty
val empty = MyTestEmpty()
2、主构造函数是类头的一部分:它跟在类名(和可选的类型参数)后。如果主构造函数没有任何注解或可见性修饰符,constructor关键字可省略,否则是必须的;
// 主体构造器(primary constructor)
class MyTest constructor(name: String) {
}
// 省略了关键字的主体构造器( 没有注解和可见性修饰符就可以省略)
class MyTest1(name: String) {
}
3、主构函数不能包含任何代码,初始化代码放在以init关键字作为前缀的初始化块中;可以有多个init,也可以没有init,init的执行顺序是根据写的顺序来的
// 主体构造器的代码必须写在init(初始化)代码块里面
class MyTest(name: String) {
val name: String
init {
this.name = name
Log.d("MyTest","my name is ${this.name}")
}
}
4、主构函数的参数可以在初始化块中使用,也可以在类体内声明的属性初始化器中使用;
5、与普通属性一样,主构函数中声明的属性也可以是可变(var )或者只读(val );
//2.类名MyTest后,主构造函数(name: String) ,无注解和可见性修饰符,constructor关键字省略
//5.主构函数中声明属性name是只读的
class MyTest(val name: String) {
//3.主构函数无代码,放在init关键字初始化块中,
private val name;
init {
Log.e("MyTest--","init1")
this.name = name
}
init {
Log.e("MyTest--","init2")
}
//4.主构函数参数可以类体内声明的属性初始化器中使用,或者初始化块中使用
val testName = name.toUpperCase()
}
//此时constructor关键字不可省略
class MyTest private constructor(val name: String) {
private val name;
init {
this.name = name
}
}
次构造函数
1、次构造函数声明必须有前缀constructor;而且是写在类体中
class MyTest {
//1.次构造函数声明前缀有constructor
constructor(name: String) {
.....
}
}
2、如果类有一个主构造函数,每个次构造函数可以使用this关键字,直接委托或者通过别的次构造函数间接委托给主构造函数;
class MyTest(var name: String) {
var mName : String = name
var mAge: Int = 0
init {
Log.e("MyTest--","init1")
this.mName = name
this.mAge = mAge
Log.e("MyTest--","init mName = $mName mAge = ${mAge} name = ${name}")
}
//2.使用this关键字,直接委托给主构造函数
constructor(name: String="1", age: Int): this(name){
Log.e("MyTest--","construct 1")
this.mName = name
this.mAge = age
Log.e("MyTest--"," mName = $mName mAge = $mAge name = $name")
}
}
3、如果一个抽象类没有声明任何(主或次)构造函数,它会有一个生成的不带参数的主构造函数,可见性是public。如果不希望类有一个公有构造函数,需要声明一个带有非默认可见性的主构造函数;
//3.主动声明一个公有构造函数,“覆盖 ”没有声明任何主次构造函数默认生成的不带参数的主构造函数
class MyPrivateTest private constructor () {
}
特别注意点
1、当只写了主构造函数,没有次构造函数,就会覆盖了默认的空造函数,此时创建对象只能通过你写的主构造函数来调用
class MyTest(var name: String){
}
//调用
var test1: MyTest = MyTest() // 这么调用默认的空的构造函数会报错
val test2: MyTest = MyTest("name") //ok
或者如果按照下面的写法就可以调用了
class MyTest(){
}
//调用
var test1: MyTest = MyTest() // ok
2、当只写了次构造函数,不写主构造函数,创建对象必须通过次构造函数调用,若不写空的参数构造函数,则调用空的构造函数会报错
open class MyTestTwo{
constructor(){ // 1
Log.e("MyTestTwo--", "MyTestTwo construct 1")
}
constructor(age: Int){ //2
Log.e("MyTestTwo--", "MyTestTwo construct 2")
}
constructor(age: Int, name:String):this(age){ //3
Log.e("MyTestTwo--", "MyTestTwo construct 3")
}
// var test1: MyTestTwo = MyTestTwo() // ok
// val test2: MyTestTwo = MyTestTwo(2) //ok
// val test3: MyTestTwo = MyTestTwo(2,"wf") //ok
}
//如不写1的空构造函数, 则test1的初始化就会报错
3、当写了既写了主构造,又写了次构造函数,可以直接调用主构造函数或者调用次构造函数创建对象。次构造函数最终都是调用主构造函数来创建对象的。
比如:下面的 constructor(age:int):this("test")是直接代理主构造函数, 而间接的constructor,比如:constructor(age: Int, name:String):this(age)这个就代理了的次constructor,而上面次constructor又代理了主构造函数。而且调用的顺序是从先执行this再执行调用的构造函数,创建对象的调用顺序先调用的主构造函数->直接代理主函数的构造函数->间接代理主函数的构造函数->我们调用的构造函数。
open class MyTestOne(var name: String){
init {
Log.e("MyTestOne--", "MyTestOne init")
}
constructor(age: Int):this("test"){
Log.e("MyTestOne--", "MyTestOne construct 1")
}
constructor(age: Int, name:String):this(age){
Log.e("MyTestOne--", "MyTestOne construct 2 $name")
}
// val test1: MyTestOne = MyTestOne("name") //ok
// val test2: MyTestOne = MyTestOne(1) //ok
// val test3: MyTestOne = MyTestOne(1,"wf") //ok
}
参考文章:
Kotlin学习系列之:Kotlin的构造函数