对象与伴生对象 #

一、单例对象 #

1.1 定义对象 #

使用 object 关键字定义单例对象:

scala
object Counter:
  private var count = 0
  
  def increment(): Int =
    count += 1
    count
  
  def current: Int = count

Counter.increment()
Counter.increment()
println(Counter.current)

1.2 工具对象 #

scala
object MathUtils:
  val PI = 3.141592653589793
  
  def square(x: Double): Double = x * x
  def cube(x: Double): Double = x * x * x
  
  def factorial(n: Int): Int =
    if n <= 1 then 1
    else n * factorial(n - 1)
  
  def isPrime(n: Int): Boolean =
    if n < 2 then false
    else !(2 to math.sqrt(n).toInt).exists(n % _ == 0)

println(MathUtils.PI)
println(MathUtils.square(5))
println(MathUtils.factorial(5))
println(MathUtils.isPrime(17))

1.3 配置对象 #

scala
object Config:
  val host = "localhost"
  val port = 8080
  val timeout = 5000
  
  def url: String = s"http://$host:$port"

println(Config.url)

二、伴生对象 #

2.1 定义伴生对象 #

与类同名且定义在同一文件中的对象:

scala
class Person(val name: String, val age: Int):
  def greet(): String = s"Hello, I'm $name"

object Person:
  def apply(name: String): Person = Person(name, 0)
  def create(name: String, age: Int): Person = Person(name, age)

val p1 = Person("Alice")
val p2 = Person.create("Bob", 25)

2.2 访问私有成员 #

伴生对象可以访问类的私有成员:

scala
class Account private (val id: String, var balance: Double):
  def deposit(amount: Double): Unit = balance += amount

object Account:
  private var nextId = 0
  
  def apply(initialBalance: Double): Account =
    nextId += 1
    new Account(s"ACC-$nextId", initialBalance)

val account = Account(1000.0)
println(account.id)

2.3 静态成员替代 #

scala
class Temperature(val celsius: Double):
  def fahrenheit: Double = celsius * 9 / 5 + 32
  def kelvin: Double = celsius + 273.15

object Temperature:
  def fromFahrenheit(f: Double): Temperature = Temperature((f - 32) * 5 / 9)
  def fromKelvin(k: Double): Temperature = Temperature(k - 273.15)
  def absoluteZero: Temperature = Temperature(-273.15)

val t1 = Temperature(25)
val t2 = Temperature.fromFahrenheit(77)
val t3 = Temperature.fromKelvin(298.15)
val t4 = Temperature.absoluteZero

println(s"${t1.celsius}°C = ${t1.fahrenheit}°F")

三、apply 方法 #

3.1 工厂方法 #

scala
class Person(val name: String, val age: Int)

object Person:
  def apply(name: String): Person = new Person(name, 0)
  def apply(name: String, age: Int): Person = new Person(name, age)

val p1 = Person("Alice")
val p2 = Person("Bob", 25)

3.2 集合创建 #

scala
object MyList:
  def apply[T](elements: T*): List[T] = elements.toList

val list = MyList(1, 2, 3, 4, 5)
println(list)

3.3 类型推断 #

scala
class Pair[A, B](val first: A, val second: B)

object Pair:
  def apply[A, B](first: A, second: B): Pair[A, B] = new Pair(first, second)

val pair = Pair("hello", 42)
println(s"${pair.first}: ${pair.second}")

四、unapply 方法 #

4.1 提取器 #

用于模式匹配提取值:

scala
class Person(val name: String, val age: Int)

object Person:
  def apply(name: String, age: Int): Person = new Person(name, age)
  def unapply(person: Person): Option[(String, Int)] =
    Some((person.name, person.age))

val person = Person("Alice", 25)

person match
  case Person(name, age) => println(s"$name is $age years old")

4.2 布尔提取器 #

scala
object Even:
  def unapply(n: Int): Boolean = n % 2 == 0

val number = 10

number match
  case Even() => println("Even number")
  case _ => println("Odd number")

4.3 序列提取器 #

scala
object FirstTwo:
  def unapply(seq: Seq[?]): Option[(?, ?)] =
    if seq.length >= 2 then Some((seq(0), seq(1)))
    else None

val list = List(1, 2, 3, 4, 5)

list match
  case FirstTwo(a, b) => println(s"First two: $a, $b")
  case _ => println("Less than two elements")

五、枚举 #

5.1 使用 sealed trait #

scala
sealed trait Direction
case object North extends Direction
case object South extends Direction
case object East extends Direction
case object West extends Direction

def turn(direction: Direction): String = direction match
  case North => "Going North"
  case South => "Going South"
  case East => "Going East"
  case West => "Going West"

5.2 Scala 3 枚举 #

scala
enum Direction:
  case North, South, East, West

def turn(direction: Direction): String = direction match
  case Direction.North => "Going North"
  case Direction.South => "Going South"
  case Direction.East => "Going East"
  case Direction.West => "Going West"

5.3 带参数的枚举 #

scala
enum Planet(mass: Double, radius: Double):
  case Mercury extends Planet(3.303e+23, 2.4397e6)
  case Venus extends Planet(4.869e+24, 6.0518e6)
  case Earth extends Planet(5.976e+24, 6.37814e6)
  case Mars extends Planet(6.421e+23, 3.3972e6)
  
  def surfaceGravity: Double = 6.67300E-11 * mass / (radius * radius)
  def surfaceWeight(otherMass: Double): Double = otherMass * surfaceGravity

println(Earth.surfaceGravity)

六、对象实战 #

6.1 工厂模式 #

scala
sealed trait Animal
case class Dog(name: String) extends Animal
case class Cat(name: String) extends Animal

object Animal:
  def create(kind: String, name: String): Option[Animal] = kind match
    case "dog" => Some(Dog(name))
    case "cat" => Some(Cat(name))
    case _ => None

val dog = Animal.create("dog", "Rex")
val cat = Animal.create("cat", "Whiskers")

6.2 构建器模式 #

scala
class Query private (
  val table: String,
  val columns: List[String],
  val where: Option[String],
  val limit: Option[Int]
)

object Query:
  def builder(table: String): QueryBuilder = QueryBuilder(table)
  
  class QueryBuilder(table: String):
    private var columns = List("*")
    private var where: Option[String] = None
    private var limit: Option[Int] = None
    
    def select(cols: String*): QueryBuilder =
      columns = cols.toList
      this
    
    def where(condition: String): QueryBuilder =
      where = Some(condition)
      this
    
    def limit(n: Int): QueryBuilder =
      limit = Some(n)
      this
    
    def build: Query = Query(table, columns, where, limit)

val query = Query.builder("users")
  .select("id", "name", "email")
  .where("age > 18")
  .limit(10)
  .build

6.3 缓存对象 #

scala
object Cache:
  private val store = scala.collection.mutable.Map.empty[String, Any]
  
  def get[T](key: String): Option[T] =
    store.get(key).map(_.asInstanceOf[T])
  
  def put[T](key: String, value: T): Unit =
    store.put(key, value)
  
  def remove(key: String): Unit =
    store.remove(key)
  
  def clear(): Unit =
    store.clear()

Cache.put("user", Map("name" -> "Alice"))
val user = Cache.get[Map[String, String]]("user")

6.4 类型类实例 #

scala
trait Show[A]:
  def show(a: A): String

object Show:
  def apply[A](implicit instance: Show[A]): Show[A] = instance
  
  given Show[Int] with
    def show(a: Int): String = a.toString
  
  given Show[String] with
    def show(a: String): String = a
  
  given Show[Boolean] with
    def show(a: Boolean): String = a.toString

def printShow[A](a: A)(using show: Show[A]): Unit =
  println(show.show(a))

printShow(42)
printShow("hello")
printShow(true)

七、对象最佳实践 #

7.1 工具方法放在对象中 #

scala
object StringUtils:
  def isBlank(s: String): Boolean = s == null || s.trim.isEmpty
  def truncate(s: String, max: Int): String = 
    if s.length <= max then s else s.take(max) + "..."

7.2 使用伴生对象提供工厂方法 #

scala
class Connection private (val host: String, val port: Int)

object Connection:
  def apply(host: String, port: Int): Connection = new Connection(host, port)
  def localhost(port: Int = 8080): Connection = new Connection("localhost", port)

7.3 使用 sealed trait + case object 表示枚举 #

scala
sealed trait Status
case object Active extends Status
case object Inactive extends Status
case object Pending extends Status

八、总结 #

对象类型 #

类型 说明
单例对象 全局唯一实例
伴生对象 与类同名的对象
case object 单例样例对象

伴生对象用途 #

用途 方法
工厂方法 apply
提取器 unapply
静态成员 val, def
类型类实例 given

下一步,让我们学习 集合概述

最后更新:2026-03-27