0%

scala构造器

Scala 构造器:主构造器与辅助构造器的灵活运用

构造器是类初始化的核心机制,Scala 在保留 Java 构造器重载特性的基础上,创新地将构造器分为主构造器辅助构造器,使类的初始化逻辑更清晰、代码更简洁。本文将详细解析 Scala 构造器的定义、使用规则及与 Java 的差异。

构造器的基本概念

Scala 中每个类都有构造器,用于初始化对象时执行必要的逻辑(如属性赋值、资源准备等)。与 Java 不同,Scala 构造器分为两类:

  • 主构造器:与类定义融为一体,是类的核心初始化逻辑。
  • 辅助构造器:通过 this 定义,用于提供额外的初始化方式,必须依赖主构造器。

主构造器

主构造器是 Scala 最具特色的设计之一,它并非显式定义在类体中,而是直接声明在类名之后,其参数列表和执行逻辑与类体紧密结合。

基本语法

1
2
3
class 类名(主构造器参数列表) {
// 类体:主构造器会执行其中的所有语句
}
参数特性:
  • 参数可通过 varval 修饰(默认是 val,只读)。
  • 若参数无修饰符,仅在类体内部可见(类似私有参数)。

示例:定义主构造器

1
2
3
4
5
6
7
8
9
class Person(var name: String, val idCard: String) {
// 主构造器会执行类体中的所有语句
println(s"初始化Person:name=$name, idCard=$idCard")

// 可以在类体中定义其他属性和方法
private var age: Int = 0

def getAge(): Int = age
}
解析:
  • 主构造器参数 namevar 修饰):会生成公开的 getter 和 setter 方法。
  • 主构造器参数 idCardval 修饰):会生成公开的 getter 方法(无 setter,不可修改)。
  • 类体中的 println 语句和 age 初始化逻辑,都会在主构造器执行时运行。

实例化对象(调用主构造器)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 主构造器私有化,仅类内部或伴生对象可调用
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 构造器重载),但必须依赖主构造器 ——每个辅助构造器的第一行必须显式调用主构造器或其他辅助构造器

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class 类名(主构造器参数) {
// 主构造器逻辑

// 辅助构造器1:def this(参数列表)
def this(参数列表1) {
this(主构造器参数) // 第一行必须调用主构造器或其他辅助构造器
// 辅助构造器1的逻辑
}

// 辅助构造器2:参数列表不同
def this(参数列表2) {
this(参数列表1) // 可调用其他辅助构造器(间接调用主构造器)
// 辅助构造器2的逻辑
}
}

示例:定义辅助构造器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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: Stringname()name_$eq(...)
val 公开 val id: Stringid()
无修饰符 类内部可见 无(或私有) 无(或私有) name: String → 仅类内访问
private var 类内部可见 私有 私有 private var age: Int → 仅类内读写

自定义 getter/setter

若默认方法不符合需求,可手动定义 getter 和 setter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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:

1
2
3
4
5
6
import scala.beans.BeanProperty

class User {
@BeanProperty var username: String = _ // 生成getUsername()和setUsername()
@BeanProperty val email: String = "test@example.com" // 生成getEmail()
}

编译后生成的 Java 方法:

1
2
3
public String getUsername() { ... }
public void setUsername(String x) { ... }
public String getEmail() { ... }

优势:确保 Scala 类能被 Java 框架正常识别和调用。

欢迎关注我的其它发布渠道