scala的类和对象

30 阅读6分钟

一、类(Class):面向对象的核心载体

类是 Scala 中描述「对象属性和行为」的模板,用于创建具体的实例(对象)。Scala 的类相比 Java 更简洁,支持参数化构造、默认值、方法简化等特性。

1. 类的基本定义与实例化

// 定义一个简单的类:包含属性(name/age)和方法(greet)
class Person(name: String, age: Int) {  // 主构造器参数(直接写在类名后)
  // 类体:可以定义方法、属性、代码块
  def greet(): String = s"Hello, I'm $name, $age years old."
  
  // 定义类的字段(用val/var修饰,对外可访问;无修饰则仅类内可见)
  val fullName: String = name  // 不可变字段(val)
  var currentAge: Int = age    // 可变字段(var)
}

// 实例化类(创建对象):无需 new 关键字(Scala 2.13+ 支持,旧版本需加 new)
val person1 = new Person("Alice", 25)  // 推荐写法(兼容所有版本)
val person2 = Person("Bob", 30)        // Scala 2.13+ 简化写法

// 访问对象的属性和方法
println(person1.fullName)    // 输出:Alice
println(person1.currentAge)  // 输出:25
println(person1.greet())     // 输出:Hello, I'm Alice, 25 years old.

// 修改可变字段
person1.currentAge = 26
println(person1.currentAge)  // 输出:26

2. 类的构造器(核心特性)

Scala 类有三种构造器,核心是主构造器,辅助构造器为补充:

(1)主构造器

  • 直接写在类名后的参数列表(class Person(name: String, age: Int))。
  • 类体中的所有非方法 / 非字段代码,都会作为主构造器的执行逻辑。
  • 支持参数默认值、访问修饰符(private/public)。
// 带默认值的主构造器
class Student(name: String = "Unknown", var score: Double = 0.0) {
  // 主构造器执行代码(创建实例时会执行)
  println(s"Student $name created, score: $score")
  
  def getGrade: String = {
    if (score >= 90) "A"
    else if (score >= 80) "B"
    else "C"
  }
}

// 实例化:省略参数则使用默认值
val stu1 = new Student("Charlie", 95.5)  // 输出:Student Charlie created, score: 95.5
val stu2 = new Student()                 // 输出:Student Unknown created, score: 0.0
println(stu1.getGrade)                   // 输出:A

(2)辅助构造器

  • def this(...) 定义,必须直接 / 间接调用主构造器(第一行代码)。
  • 用于提供多套实例化参数方案。
class User(username: String, email: String) {
  // 辅助构造器1:仅传用户名,邮箱用默认值
  def this(username: String) = this(username, s"$username@default.com")
  
  // 辅助构造器2:无参数,全用默认值
  def this() = this("guest")
  
  def showInfo: String = s"Username: $username, Email: $email"
}

// 用不同构造器实例化
val user1 = new User("alice123", "alice@test.com")
val user2 = new User("bob456")
val user3 = new User()

println(user1.showInfo)  // 输出:Username: alice123, Email: alice@test.com
println(user2.showInfo)  // 输出:Username: bob456, Email: bob456@default.com
println(user3.showInfo)  // 输出:Username: guest, Email: guest@default.com

3. 类的访问修饰符

Scala 支持 private/protected/public(默认 public),且支持更精细的访问控制:

class Employee(private val id: String, var name: String) {
  // private 方法:仅类内可调用
  private def checkId: Boolean = id.nonEmpty
  
  // 公共方法:对外暴露逻辑
  def validate: String = {
    if (checkId) s"Employee $name is valid"
    else "Invalid employee ID"
  }
}

val emp = new Employee("E123", "David")
println(emp.name)       // 可访问(public)
// println(emp.id)      // 编译报错(private)
// println(emp.checkId) // 编译报错(private)
println(emp.validate)   // 输出:Employee David is valid

二、对象(Object):Scala 的单例与工具载体

Scala 没有 static 关键字,而是通过 object 实现单例、静态方法 / 属性、工具类等功能,主要分为单例对象伴生对象

1. 单例对象(Standalone Object)

  • object 定义,全局唯一实例(JVM 加载时自动创建)。
  • 常用于定义工具方法、常量、程序入口(main 方法)。
// 定义单例对象(工具类)
object MathUtils {
  // 常量(相当于 Java 的 static final)
  val PI: Double = 3.1415926
  
  // 工具方法(相当于 Java 的 static 方法)
  def circleArea(radius: Double): Double = PI * radius * radius
  
  // 程序入口(main 方法必须定义在 object 中)
  def main(args: Array[String]): Unit = {
    println(s"PI = $PI")
    println(s"Circle area (r=2): ${circleArea(2)}")  // 输出:12.5663704
  }
}

// 调用单例对象的属性/方法(无需实例化)
println(MathUtils.PI)               // 输出:3.1415926
println(MathUtils.circleArea(3))    // 输出:28.2743334

2. 伴生对象(Companion Object)

  • 与类同名、同文件的 object,称为该类的伴生对象;对应的类称为伴生类。
  • 伴生对象和伴生类可以互相访问对方的 private 成员(核心优势)。
  • 常用于定义工厂方法(简化类的实例化)、隐式转换等。
// 伴生类(普通类)
class Person private (val name: String, val age: Int) {  // 主构造器私有化
  private def secret: String = "I love Scala"
}

// 伴生对象(与类同名、同文件)
object Person {
  // 工厂方法:简化实例化(对外隐藏构造器细节)
  def apply(name: String): Person = new Person(name, 18)  // 访问私有构造器
  def apply(name: String, age: Int): Person = new Person(name, age)
  
  // 访问伴生类的私有方法
  def getSecret(person: Person): String = person.secret
}

// 用伴生对象的 apply 方法实例化(无需 new)
val p1 = Person("Alice")        // 等价于 Person.apply("Alice")
val p2 = Person("Bob", 25)      // 等价于 Person.apply("Bob", 25)

println(p1.name)                // 输出:Alice
println(Person.getSecret(p1))   // 输出:I love Scala(访问私有方法)
// val p3 = new Person("Charlie", 30)  // 编译报错(构造器私有化)

关键:apply 方法的特殊作用

伴生对象中的 apply 方法是 Scala 的语法糖:

  • 调用 Person(参数) 等价于调用 Person.apply(参数)
  • 无需写 new,让类的实例化像调用函数一样简洁,是 Scala 集合(如 List(1,2,3))的核心实现方式。

3. 扩展:case class 与伴生对象

case class(样例类)是 Scala 特有的便捷类,自动生成伴生对象、apply 方法、equals/hashCode 等,常用于数据封装:

// 定义样例类(自动生成伴生对象、apply方法、toString等)
case class Book(title: String, author: String, price: Double)

// 实例化(无需 new,自动调用伴生对象的 apply 方法)
val book1 = Book("Scala编程", "Martin Odersky", 89.0)
val book2 = Book("Java编程思想", "Bruce Eckel", 108.0)

// 自动生成 toString 方法
println(book1)  // 输出:Book(Scala编程,Martin Odersky,89.0)

// 自动生成 equals 方法(值比较,而非引用比较)
val book3 = Book("Scala编程", "Martin Odersky", 89.0)
println(book1 == book3)  // 输出:true

三、类的继承(Inheritance)

Scala 支持单继承(extends),通过 override 重写父类方法 / 属性,核心规则:

  1. 父类构造器必须在子类中被调用(写在 extends 后)。
  2. 重写方法 / 属性必须加 override 关键字。
  3. final 修饰的类 / 方法不能被继承 / 重写。
// 父类(基类)
class Animal(name: String) {
  def makeSound: String = "Some sound"
  val category: String = "Animal"
}

// 子类(继承 Animal)
class Dog(name: String) extends Animal(name) {  // 调用父类构造器
  // 重写父类方法
  override def makeSound: String = "Woof Woof"
  
  // 重写父类属性
  override val category: String = "Dog"
}

// 测试
val dog = new Dog("Buddy")
println(dog.name)       // 输出:Buddy(继承父类属性)
println(dog.makeSound)  // 输出:Woof Woof(重写方法)
println(dog.category)   // 输出:Dog(重写属性)

四、抽象类(Abstract Class)

抽象类用 abstract 修饰,包含未实现的抽象方法 / 属性,不能直接实例化,需子类实现抽象成员:

// 抽象类
abstract class Shape {
  // 抽象属性(无初始值)
  val color: String
  
  // 抽象方法(无实现)
  def area: Double
}

// 子类实现抽象类
class Circle(color: String, radius: Double) extends Shape {
  // 实现抽象属性
  override val color: String = color
  
  // 实现抽象方法
  override def area: Double = Math.PI * radius * radius
}

val circle = new Circle("Red", 2.0)
println(circle.color)  // 输出:Red
println(circle.area)   // 输出:12.566370614359172

总结

  1. 类(Class) :是创建实例的模板,主构造器写在类名后,辅助构造器用 def this 定义,支持访问修饰符和继承。

  2. 对象(Object)

    • 单例对象:全局唯一实例,用于工具方法、常量、程序入口(main)。
    • 伴生对象:与类同名同文件,可访问类的私有成员,apply 方法简化实例化。
  3. 核心语法糖

    • case class 自动生成伴生对象、apply/equals 等方法,适合数据封装。
    • 伴生对象的 apply 方法让类实例化像调用函数一样简洁。
  4. 继承规则:Scala 单继承,重写需加 override,抽象类需子类实现抽象成员。