一、概念
- 构造函数分为 主构造函数 和 次构造函数。主构造只有一个,次构造可以有多个。
- 不声明任何构造时,Kotlin会给类默认增加一个空参主构造。
- 执行顺序:主构造 > init代码块 > 次构造
二、主构造函数
有可见性修饰或注解的时候,constructor 不能省略。主构造没有函数体,需要编写逻辑的话写在 init{} 代码块中。
//构造有可见性修饰或注解的时候,关键字不能省略
class Demo private @Inject constructor() {}
//推荐下面写法(定义无参主构造,写次构造需要每次都委托)
class Demo() {}
//主构造无参数可以省略括号(未定义次构造时,类默认提供一个无参主构造)
class Demo {}
//无类体可以省略大括号
class Demo
主构造中参数的使用:
- 如果使用 var/val 修饰参数,会自动声明并初始化同名成员属性。(推荐,其实就是自动使用了init{ } )
- 手动使用 init{} 代码块手动声明并初始化。可以存在多个 init{} 代码块,执行顺序和声明顺序一致。
//方式一:使用var、val自动声明初始化
class Demo(var id: Int, var name: String) {
//实在需要也可以再写init{}代码块做一些逻辑
init{
println("$id 、 $name")
}
}
//方式二:使用init{}代码块手动声明初始化
//id、name只能在构造和成员属性中调用,可以声明同名成员属性,无法在成员函数中调用
class Demo(id: Int, name: String) {
var id? = null
var name = name
init {
= id
}
}
三、次构造函数
- 次构造不能像主构造那样使用 val/var,只能手动声明并初始化成员属性,由于次构造会改变成员属性的值,因此该成员属性只能手动声明为var。
- 有声明主构造时,次构造必须使用委托的方式直接或间接调用主构造。
3.1 未声明主构造的情况
写法和Java类似。次构造不能委托给主构造,因为没有主构造。
class Demo {
var id: Long? = null
var name: String? = null
var age: Int? = null
constructor(){}
constructor(id: Long) {
= id
}
constructor(name: String, age: Int) {
= name
= age
}
}
var aa = Demo()
var bb = Demo(222)
var cc = Demo("张三",18)
3.2 声明空参主构造的情况
次构造使用委托调用主构造。
//有空参主构造的情况下,定义空参次构造报错重载冲突,若委托给空参this()报错调用链无限循环
//使用委托的方式调用主构造
class Demo() {
var id: Long? = null
var name: String? = null
var age: Int? = null
constructor(id: Long) : this() {
= id
}
constructor( name: String, age: Int) : this() {
= name
= age
}
}
var aa = Demo()
var bb = Demo(222)
var cc = Demo("张三",18)
3.3 声明有参主构造的情况
主构造使用默认参数简化次构造重载。
//主构造中的形参id未提供默认值,因此不能创建空参实例
//即便id提供默认值了,声明空参次构造委托空参this()报错调用链无限循环
//但可以委托有参数 this(10086) 或 this(name = ""),相当于调用了主构造
class Demo(var id: Long, var name: String = "", var age: Int = 0) {
var sex: Boolean? = null
//这样可以声明空参构造,也就可以创建空参实例了
constructor() : this(10086) {}
//主构造未包含字段sex,声明一个次构造来使用它,依然使用委托来调用主构造
//不管次构造中有多少Long类型参数,往this()里面传一个进去就行
//往this()里面传参要按照参数列表顺序,因为主构造里有默认参数,可以使用命名参数的形式部分传参
constructor(sex: Boolean, aa: Long, bb: Long, cc: String) : this(bb) {
= sex
//主构造使用var/val后就默认有了name属性可以使用,不用再手动声明并初始化
//cc可以传递进this()中,就不用在这里手动赋值name了
= cc
}
}
var aa = Demo(10086, "张三")
var bb = Demo(10086, age = 18) //不使用命名参数,第二个得传name
var cc = Demo(false,10086,10010,"张三")
3.4 次构造之间相互调用的情况
也使用委托简化。
//Java
class Demo {
public Demo(long id) {
= id;
}
public Demo(String name) {
this(()); //使用this()访问重载的构造
= name;
}
}
//Kotlin 无主构造的情况,写法和Java类似
class Demo{
constructor(id:Long){
= id
}
constructor(name:String):this(().toLong()){
= name
}
}
//Kotlin 有主构造的情况
class Demo(val id: Long) {
//直接调用主构造
constructor(name: String) : this(().toLong()) {
= name
}
//调用了上面的构造,间接调用主构造
constructor(name: String, sex: Boolean) : this(name) {
= sex
}
}