在 Kotlin 中,构造函数是用于创建类对象的核心部分。Kotlin 提供了 主构造函数(Primary Constructor)和 次构造函数(Secondary Constructor)两种方式来定义构造函数。它还支持初始化代码块和默认参数等特性。
主构造函数是直接定义在 类头部 的构造函数,它可以为类的属性提供默认初始化。
class Person(val name: String, var age: Int)
示例 1:带有属性的主构造函数
class Person(val name: String, var age: Int)
fun main() {
val person = Person("Alice", 25)
println("Name: ${person.name}, Age: ${person.age}")
}
解释:
name 和 age 是类的属性,并且通过构造函数初始化。
主构造函数的参数自动成为 类的成员变量(带 val 或 var)。
示例 2:添加初始化逻辑
你可以使用 init 初始化块 来编写初始化逻辑。
class Person(val name: String, var age: Int) {
init {
println("A person named $name is created.")
}
}
fun main() {
val person = Person("Bob", 30)
}
解释:
init 块在对象创建时自动执行,可以用于执行初始化逻辑。
示例 3:默认参数
构造函数中的参数可以有 默认值。
class Person(val name: String = "Unknown", var age: Int = 0)
fun main() {
val person1 = Person()
val person2 = Person("Alice", 25)
println("${person1.name}, ${person1.age}") // 输出: Unknown, 0
println("${person2.name}, ${person2.age}") // 输出: Alice, 25
}
解释:
当参数有默认值时,可以选择在创建对象时不传入该参数。
次构造函数用于提供多种初始化方式。如果主构造函数不能满足你的需求,可以定义多个次构造函数。
class Person {
var name: String
var age: Int
// 次构造函数
constructor(name: String, age: Int) {
this.name = name
this.age = age
}
}
示例:带次构造函数的类
class Person {
var name: String
var age: Int
// 次构造函数
constructor(name: String, age: Int) {
this.name = name
this.age = age
}
}
fun main() {
val person = Person("Charlie", 28)
println("Name: ${person.name}, Age: ${person.age}")
}
解释:
使用 this 引用当前对象的属性,并通过次构造函数初始化它们。
当类有主构造函数时,次构造函数必须调用主构造函数。你可以通过 this 调用来实现。
class Person(val name: String, var age: Int) {
// 次构造函数
constructor(name: String) : this(name, 0) {
println("Created a person named $name with default age.")
}
}
fun main() {
val person = Person("Alice")
println("Name: ${person.name}, Age: ${person.age}")
}
解释:
次构造函数通过 this 调用了主构造函数,并为 age 赋予默认值 0。
Kotlin 允许类没有构造函数,这在一些简单数据模型中很常见。
class EmptyClass
init 块 总是在 主构造函数之后执行。
次构造函数 调用 主构造函数 后,才会执行次构造函数的内容。
示例:初始化顺序
class Person(val name: String) {
var age: Int = 0
init {
println("Initialized with name: $name")
}
constructor(name: String, age: Int) : this(name) {
this.age = age
println("Initialized with age: $age")
}
}
fun main() {
val person = Person("Alice", 25)
}
输出:
csharp
Initialized with name: Alice
Initialized with age: 25
在 Kotlin 中,如果一个类有 多个次构造函数,每个次构造函数都必须最终调用到 主构造函数(如果存在)。
多个次构造函数之间可以相互调用,但最终它们都必须连接到主构造函数。
每个 次构造函数 可以通过 this 调用同类中的其他次构造函数。
但所有次构造函数最终必须通过链条式调用(直接或间接)到 主构造函数。
示例1:多个次构造函数相互调用
class Person(val name: String, var age: Int) {
constructor(name: String) : this(name, 0) {
println("Called constructor1 with name: $name")
}
constructor() : this("name", 0) {
println("Called constructor2 with default values.")
}
}
fun main() {
val person1 = Person("Alice")
println("Person1 -> Name: ${person1.name}, Age: ${person1.age}")
val person2 = Person()
println("Person2 -> Name: ${person2.name}, Age: ${person2.age}")
var person3 = Person("haha", 12)
println("person3->Name:${person3.name},Age:${person3.age}")
}
输出:
Called constructor1 with name: Alice
Person1 -> Name: Alice, Age: 0
Called constructor2 with default values.
Person2 -> Name: name, Age: 0
person3->Name:haha,Age:12
主构造函数:Person(val name: String, var age: Int)
name
和 age
。次构造函数 1:constructor(name: String) : this(name, 0)
name
参数,并调用主构造函数,默认将 age
设置为 0。会打印 "Called constructor1 with name: $name"
。次构造函数 2:constructor() : this("name", 0)
name
默认设置为 "name"
,age
默认设置为 0。会打印 "Called constructor2 with default values."
。创建 person1
:val person1 = Person("Alice")
this(name, 0)
,所以 name
被设置为 "Alice"
,age
被设置为 0。Called constructor1 with name: Alice
Person1 -> Name: Alice, Age: 0
创建 person2
:val person2 = Person()
this("name", 0)
,所以 name
被设置为 "name"
,age
被设置为 0。Called constructor2 with default values.
Person2 -> Name: name, Age: 0
创建 person3
:var person3 = Person("haha", 12)
name
被设置为 "haha"
,age
被设置为 12。person3->Name: haha, Age: 12
person1
调用了次构造函数 1。person2
调用了次构造函数 2。person3
调用了主构造函数。示例2:多个次构造函数相互调用
class Person(val name: String, var age: Int) {
constructor(name: String) : this(name, 0) {
println("Called constructor1 with name: $name")
}
constructor() : this("name") {
println("Called constructor2 with default values.")
}
}
fun main() {
val person1 = Person("Alice")
println("Person1 -> Name: ${person1.name}, Age: ${person1.age}")
val person2 = Person()
println("Person2 -> Name: ${person2.name}, Age: ${person2.age}")
var person3 = Person("haha", 12)
println("person3->Name:${person3.name},Age:${person3.age}")
}
输出:
Called constructor1 with name: Alice
Person1 -> Name: Alice, Age: 0
Called constructor1 with name: name
Called constructor2 with default values.
Person2 -> Name: name, Age: 0
person3->Name:haha,Age:12
主构造函数:Person(val name: String, var age: Int)
name
和 age
。次构造函数 1:constructor(name: String) : this(name, 0)
name
,并调用主构造函数,默认将 age
设置为 0。次构造函数 2:constructor() : this("name")
this("name")
会传递 "name"
作为参数,然后触发次构造函数 1。Person()
当调用 val person2 = Person()
时,以下步骤会被执行:
Person()
是无参数的构造函数,首先进入次构造函数 2。this("name")
会调用次构造函数 1,将 "name"
作为参数传递。this(name, 0)
会被执行,将 name
设置为 "name"
,并将 age
设置为 0。所以,当调用 val person2 = Person()
时,实际的调用顺序是:
"Called constructor2 with default values."
。"Called constructor1 with name: name"
。最终,person2
的 name
为 "name"
,age
为 0。
val person2 = Person()
先进入次构造函数 2,然后通过 this("name")
进入次构造函数 1,最后调用主构造函数,形成了一个调用链。这个设计使得构造函数可以方便地进行重用和扩展。
你可以灵活地在次构造函数间传递不同的参数。以下例子展示了多个次构造函数之间的调用。
示例:动态传递参数
class Person(val name: String, var age: Int) {
// 次构造函数 1:只有 name
constructor(name: String) : this(name, 0) {
println("Initialized with name: $name, default age: 0")
}
// 次构造函数 2:传入 name 和 age
constructor(name: String, birthYear: Int, currentYear: Int) : this(name, currentYear - birthYear) {
println("Calculated age for $name: ${currentYear - birthYear}")
}
}
fun main() {
val person1 = Person("Bob")
println("Person1 -> Name: ${person1.name}, Age: ${person1.age}")
val person2 = Person("Charlie", 1995, 2024)
println("Person2 -> Name: ${person2.name}, Age: ${person2.age}")
}
输出:
Initialized with name: Bob, default age: 0
Person1 -> Name: Bob, Age: 0
Calculated age for Charlie: 29
Person2 -> Name: Charlie, Age: 29
解释:
Person(“Bob”) 调用了次构造函数 1,并最终到达主构造函数。
Person(“Charlie”, 1995, 2024) 通过次构造函数 2 动态计算了年龄,并最终调用了主构造函数。
多个次构造函数可以通过 this 相互调用,但必须最终到达 主构造函数。
如果需要灵活处理不同的初始化参数,多个次构造函数之间的链式调用是很有用的。
调用顺序:
从被调用的次构造函数开始;每个次构造函数通过 this 逐级调用到主构造函数;一旦调用链到达主构造函数,开始执行初始化逻辑和 init 块。
你可以为构造函数设置 访问修饰符(如 private、protected 等)。
这对于控制类的实例化很有用。
class Person private constructor(val name: String)
解释:
使用 private 构造函数后,该类只能在内部创建实例,外部无法实例化。
数据类自动生成 equals()、hashCode()、toString() 等函数,主构造函数中的参数会自动成为类的属性。
data class Person(val name: String, val age: Int)
fun main() {
val person1 = Person("Alice", 25)
val person2 = Person("Alice", 25)
println(person1 == person2) // 输出: true
println(person1) // 输出: Person(name=Alice, age=25)
}
主构造函数:定义在类头部,通常用于初始化属性,支持 init 块和默认参数。
次构造函数:用于提供多种初始化方式,必须调用主构造函数(若存在)。
init 块:用于在构造函数完成之后执行初始化逻辑。
访问修饰符:控制构造函数的访问权限。