scala构造器
Scala 构造器:主构造器与辅助构造器的灵活运用
构造器是类初始化的核心机制,Scala 在保留 Java 构造器重载特性的基础上,创新地将构造器分为主构造器和辅助构造器,使类的初始化逻辑更清晰、代码更简洁。本文将详细解析 Scala 构造器的定义、使用规则及与 Java 的差异。
构造器的基本概念
Scala 中每个类都有构造器,用于初始化对象时执行必要的逻辑(如属性赋值、资源准备等)。与 Java 不同,Scala 构造器分为两类:
- 主构造器:与类定义融为一体,是类的核心初始化逻辑。
- 辅助构造器:通过
this定义,用于提供额外的初始化方式,必须依赖主构造器。
主构造器
主构造器是 Scala 最具特色的设计之一,它并非显式定义在类体中,而是直接声明在类名之后,其参数列表和执行逻辑与类体紧密结合。
基本语法
class 类名(主构造器参数列表) {
// 类体:主构造器会执行其中的所有语句
}
参数特性:
- 参数可通过
var或val修饰(默认是val,只读)。 - 若参数无修饰符,仅在类体内部可见(类似私有参数)。
示例:定义主构造器
class Person(var name: String, val idCard: String) {
// 主构造器会执行类体中的所有语句
println(s"初始化Person:name=$name, idCard=$idCard")
// 可以在类体中定义其他属性和方法
private var age: Int = 0
def getAge(): Int = age
}
解析:
- 主构造器参数
name(var修饰):会生成公开的 getter 和 setter 方法。 - 主构造器参数
idCard(val修饰):会生成公开的 getter 方法(无 setter,不可修改)。 - 类体中的
println语句和age初始化逻辑,都会在主构造器执行时运行。
实例化对象(调用主构造器)
object PersonDemo {
def main(args: Array[String]): Unit = {
// 实例化时直接传递主构造器参数
val person = new Person("张三", "110101199001011234")
// 输出:初始化Person:name=张三, idCard=110101199001011234
// 访问属性(调用getter)
println(person.name) // 输出:张三
println(person.idCard) // 输出:110101199001011234
// 修改var属性(调用setter)
person.name = "李四"
println(person.name) // 输出:李四
}
}
私有化主构造器
若需限制主构造器的访问(如单例模式),可在参数列表前加 private:
// 主构造器私有化,仅类内部或伴生对象可调用
class Person private(name: String, idCard: String) {
println(s"私有构造器初始化:$name")
}
// 伴生对象(可访问私有构造器)
object Person {
def apply(name: String, idCard: String): Person = {
new Person(name, idCard) // 合法:伴生对象可调用私有主构造器
}
}
// 测试
val p = Person("王五", "123") // 通过伴生对象创建实例
辅助构造器
辅助构造器用于提供额外的初始化方式(类似 Java 构造器重载),但必须依赖主构造器 ——每个辅助构造器的第一行必须显式调用主构造器或其他辅助构造器。
基本语法
class 类名(主构造器参数) {
// 主构造器逻辑
// 辅助构造器1:def this(参数列表)
def this(参数列表1) {
this(主构造器参数) // 第一行必须调用主构造器或其他辅助构造器
// 辅助构造器1的逻辑
}
// 辅助构造器2:参数列表不同
def this(参数列表2) {
this(参数列表1) // 可调用其他辅助构造器(间接调用主构造器)
// 辅助构造器2的逻辑
}
}
示例:定义辅助构造器
class Student(name: String, id: String) { // 主构造器
println(s"主构造器初始化:$name, $id")
var score: Double = 0.0 // 成绩属性
// 辅助构造器1:增加成绩参数
def this(name: String, id: String, score: Double) {
this(name, id) // 调用主构造器
this.score = score // 初始化成绩
println(s"辅助构造器1:成绩=$score")
}
// 辅助构造器2:无id参数(默认id为"0000")
def this(name: String) {
this(name, "0000") // 调用主构造器(传递默认id)
println("辅助构造器2:使用默认id")
}
}
// 测试
object StudentDemo {
def main(args: Array[String]): Unit = {
val s1 = new Student("赵六", "2023001") // 调用主构造器
// 输出:主构造器初始化:赵六, 2023001
val s2 = new Student("孙七", "2023002", 95.5) // 调用辅助构造器1
// 输出:
// 主构造器初始化:孙七, 2023002
// 辅助构造器1:成绩=95.5
val s3 = new Student("周八") // 调用辅助构造器2
// 输出:
// 主构造器初始化:周八, 0000
// 辅助构造器2:使用默认id
}
}
核心规则
- 必须调用主构造器:辅助构造器第一行必须通过
this(...)调用主构造器或其他辅助构造器(最终间接调用主构造器)。 - 参数列表唯一:不同辅助构造器的参数列表必须不同(个数或类型不同),实现重载。
- 不能直接调用父类构造器:只有主构造器可以调用父类构造器(通过
extends语法,后续章节详解)。
构造器与属性的 getter/setter 方法
Scala 会根据构造器参数的修饰符(var/val/ 无修饰符)自动生成 getter 和 setter 方法,规则如下:
| 参数修饰符 | 可见性 | getter 方法 | setter 方法 | 示例 |
|---|---|---|---|---|
var |
公开 | 有 | 有 | var name: String → name() 和 name_$eq(...) |
val |
公开 | 有 | 无 | val id: String → id() |
| 无修饰符 | 类内部可见 | 无(或私有) | 无(或私有) | name: String → 仅类内访问 |
private var |
类内部可见 | 私有 | 私有 | private var age: Int → 仅类内读写 |
自定义 getter/setter
若默认方法不符合需求,可手动定义 getter 和 setter:
class Person {
private var _age: Int = 0 // 私有字段(约定用下划线开头)
// 自定义getter
def age: Int = _age
// 自定义setter(方法名固定为“属性名_=”)
def age_=(newAge: Int): Unit = {
if (newAge > 0) { // 增加校验逻辑
_age = newAge
} else {
println("年龄必须为正数")
}
}
}
// 测试
val p = new Person()
p.age = 20 // 调用自定义setter
println(p.age) // 调用自定义getter → 20
p.age = -5 // 触发校验 → 输出“年龄必须为正数”
与 JavaBean 的兼容性
Java 框架(如 Spring、Hibernate)通常依赖标准的 getXxx() 和 setXxx() 方法。Scala 提供 @BeanProperty 注解,可自动生成符合 Java 规范的 getter/setter:
import scala.beans.BeanProperty
class User {
var username: String = _ // 生成getUsername()和setUsername()
val email: String = "test@example.com" // 生成getEmail()
}
编译后生成的 Java 方法:
public String getUsername() { ... }
public void setUsername(String x) { ... }
public String getEmail() { ... }
优势:确保 Scala 类能被 Java 框架正常识别和调用。