Kotlin学习-构造函数

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
}

猜你喜欢

转载自blog.csdn.net/liujun3512159/article/details/129154365