scala构造器

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

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

构造器的基本概念

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

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

主构造器

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

基本语法

class 类名(主构造器参数列表) {
  // 类体:主构造器会执行其中的所有语句
}
参数特性:
  • 参数可通过 varval 修饰(默认是 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
}
解析:
  • 主构造器参数 namevar 修饰):会生成公开的 getter 和 setter 方法。
  • 主构造器参数 idCardval 修饰):会生成公开的 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: Stringname()name_$eq(...)
val 公开 val id: Stringid()
无修饰符 类内部可见 无(或私有) 无(或私有) 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 {
  @BeanProperty var username: String = _  // 生成getUsername()和setUsername()
  @BeanProperty val email: String = "test@example.com"  // 生成getEmail()
}

编译后生成的 Java 方法:

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

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