scala中的类和对象

30 阅读4分钟

一、类(Class):对象的模板

类是 创建对象的蓝图,定义了对象的属性(成员变量)和行为(成员方法)。Scala 中的类支持:构造器(主构造器 + 辅助构造器)、继承、封装、多态等 OOP 特性,同时简化了语法(如自动生成 getter/setter)。

1. 基本定义与实例化

// 定义类:主构造器直接写在类名后(参数列表可选)
class Person(name: String, var age: Int) { // name: 私有不可变(无var/val);age: 公有可变(var)
  // 类体:初始化逻辑、成员方法、辅助构造器等
  println(s"创建 Person 实例:$name, $age") // 主构造器执行时会运行类体中的语句

  // 成员方法
  def introduce(): String = s"我是 $name,今年 $age 岁"

  // 辅助构造器(必须调用主构造器或其他辅助构造器,以 this 开头)
  def this(name: String) = this(name, 18) // 无年龄时默认18岁
}

// 实例化类(无需 new 关键字,Scala 2.13+ 支持;低版本需加 new)
val p1 = new Person("张三", 25) // 输出:创建 Person 实例:张三, 25
val p2 = Person("李四") // 调用辅助构造器,默认年龄18

// 访问成员
println(p1.age) // 25(var 修饰的属性可直接访问,自动生成 setter)
p1.age = 26 // 修改 age(var 生成 setter)
// println(p1.name) // 编译报错:name 无 var/val 修饰,默认是私有成员,仅类内部可访问
println(p1.introduce()) // 输出:我是 张三,今年 26 岁

2. 类的核心特性

特性说明
主构造器类名后的参数列表(class A(a: Int, val b: String)),类体中无函数体的代码均为主构造器逻辑
字段修饰符- 无 var/val:私有不可变(仅类内部访问)- val:公有不可变(仅生成 getter)- var:公有可变(生成 getter + setter)
辅助构造器用 def this(...) 定义,必须先调用主构造器(this(...))或其他辅助构造器
私有成员用 private 修饰(默认私有范围是类和伴生对象),private[this] 仅当前实例可访问
继承用 extends 继承类,子类必须调用父类构造器;重写方法需加 override 关键字

示例:继承与重写

// 父类
class Animal(val name: String) {
  def makeSound(): String = "未知叫声"
}

// 子类(继承 Animal,必须调用父类构造器)
class Dog(name: String) extends Animal(name) {
  override def makeSound(): String = "汪汪汪" // 重写方法必须加 override
}

val dog = Dog("旺财")
println(dog.name) // 继承自父类的 val 字段,可访问
println(dog.makeSound()) // 输出:汪汪汪

二、对象(Object):单例与工具容器

Scala 中没有 static 关键字,对象(Object)  承担了两种核心角色:单例对象 和 伴生对象。对象是 懒加载的单例实例(首次访问时初始化,全局唯一)。

1. 单例对象(独立对象)

独立于类的对象,本质是全局唯一的实例,常用于:工具类、常量定义、程序入口(main 方法)。

// 单例对象:工具类(全局唯一,无需实例化)
object MathUtils {
  // 常量(用 val 定义,类似 Java 的 static final)
  val PI: Double = 3.1415926

  // 工具方法(类似 Java 的 static 方法)
  def circleArea(radius: Double): Double = PI * radius * radius
}

// 直接通过对象名访问,无需 new
println(MathUtils.PI) // 3.1415926
println(MathUtils.circleArea(2)) // 12.5663704

// 程序入口:Scala 程序的 main 方法必须定义在单例对象中
object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello, Scala!")
  }
}

2. 伴生对象(Companion Object)

与某个类同名的对象,称为该类的 伴生对象;对应的类称为 伴生类。两者必须定义在同一个源文件中,核心特性:相互访问私有成员(无需 getter/setter)。

伴生对象的典型用途:

  • 替代 Java 的 static 方法 / 常量(如工厂方法、静态工具)
  • 与伴生类共享私有状态
// 伴生类(Class)
class Student(private val id: Int, val name: String) {
  def printId(): Unit = {
    // 伴生类可访问伴生对象的私有成员
    println(s"学生ID:$id,学校:${Student.SCHOOL_NAME}")
  }
}

// 伴生对象(Object,与类同名)
object Student {
  // 私有常量(仅伴生类和伴生对象可访问)
  private val SCHOOL_NAME: String = " Scala 大学"

  // 工厂方法(替代 new,灵活创建实例)
  def apply(name: String): Student = new Student(generateId(), name)

  // 私有工具方法(仅伴生类和伴生对象可访问)
  private def generateId(): Int = scala.util.Random.nextInt(1000)
}

// 使用伴生对象的工厂方法创建实例(无需 new)
val s1 = Student("王五") // 调用 apply 方法,等价于 Student.apply("王五")
s1.printId() // 输出:学生ID:xxx(随机数),学校:Scala 大学

// 伴生对象可访问伴生类的私有成员
val s2 = new Student(1001, "赵六")
// println(s2.id) // 编译报错:id 是伴生类的私有成员,外部不可访问
// 但伴生对象可访问(需通过实例)
object StudentHelper {
  def getStudentId(student: Student): Int = student.id // 编译报错:StudentHelper 不是伴生对象,无法访问私有 id
}