Skip to content

Scala 类和对象

类和对象是面向对象编程的核心概念。本章将详细介绍 Scala 中类的定义、对象的创建、继承、多态等面向对象编程的基本概念。

类的定义

基本类定义

scala
// 基本类定义
class Person {
  // 类体
}

// 创建对象
val person = new Person()

// 带参数的类
class Student(name: String, age: Int) {
  // 主构造函数参数自动成为类的字段
  def introduce(): String = s"Hi, I'm $name and I'm $age years old"
}

val student = new Student("Alice", 20)
println(student.introduce())

构造函数参数

scala
// val 参数:不可变字段
class Person(val name: String, val age: Int) {
  // name 和 age 自动成为公共的不可变字段
}

val person = new Person("Bob", 25)
println(person.name)  // 可以访问
println(person.age)   // 可以访问
// person.age = 26    // 编译错误:不能修改

// var 参数:可变字段
class Counter(var count: Int) {
  def increment(): Unit = count += 1
  def decrement(): Unit = count -= 1
}

val counter = new Counter(0)
counter.increment()
println(counter.count)  // 1
counter.count = 10      // 可以直接修改

私有参数

scala
// 私有参数:不生成字段
class BankAccount(private var balance: Double) {
  
  def deposit(amount: Double): Unit = {
    if (amount > 0) balance += amount
  }
  
  def withdraw(amount: Double): Boolean = {
    if (amount > 0 && amount <= balance) {
      balance -= amount
      true
    } else {
      false
    }
  }
  
  def getBalance: Double = balance  // 只读访问
}

val account = new BankAccount(1000.0)
account.deposit(500.0)
println(account.getBalance)  // 1500.0
// println(account.balance)  // 编译错误:私有字段

方法和字段

实例方法

scala
class Calculator {
  def add(x: Int, y: Int): Int = x + y
  
  def multiply(x: Int, y: Int): Int = x * y
  
  // 方法可以访问类的字段
  private var history: List[String] = List()
  
  def addWithHistory(x: Int, y: Int): Int = {
    val result = x + y
    history = s"$x + $y = $result" :: history
    result
  }
  
  def getHistory: List[String] = history.reverse
}

val calc = new Calculator()
calc.addWithHistory(3, 4)
calc.addWithHistory(5, 6)
println(calc.getHistory)  // List("3 + 4 = 7", "5 + 6 = 11")

字段初始化

scala
class Person(firstName: String, lastName: String) {
  // 计算字段
  val fullName: String = s"$firstName $lastName"
  
  // 延迟初始化字段
  lazy val initials: String = {
    println("Computing initials...")
    s"${firstName.head}.${lastName.head}."
  }
  
  // 可变字段
  var nickname: String = firstName
  
  // 私有字段
  private var id: Int = Person.nextId()
  
  def getId: Int = id
}

object Person {
  private var currentId = 0
  
  private def nextId(): Int = {
    currentId += 1
    currentId
  }
}

val person = new Person("John", "Doe")
println(person.fullName)   // "John Doe"
println(person.initials)   // 此时才计算
println(person.getId)      // 1

辅助构造函数

scala
class Person(val name: String, val age: Int) {
  
  // 辅助构造函数必须调用主构造函数或其他辅助构造函数
  def this(name: String) = {
    this(name, 0)  // 调用主构造函数
  }
  
  def this() = {
    this("Unknown")  // 调用上面的辅助构造函数
  }
  
  override def toString: String = s"Person($name, $age)"
}

// 使用不同的构造函数
val person1 = new Person("Alice", 25)
val person2 = new Person("Bob")
val person3 = new Person()

println(person1)  // Person(Alice, 25)
println(person2)  // Person(Bob, 0)
println(person3)  // Person(Unknown, 0)

对象(Object)

单例对象

scala
// 单例对象
object MathUtils {
  val PI: Double = 3.14159
  
  def square(x: Double): Double = x * x
  
  def circleArea(radius: Double): Double = PI * square(radius)
  
  private var callCount = 0
  
  def getCallCount: Int = {
    callCount += 1
    callCount
  }
}

// 使用单例对象
println(MathUtils.PI)
println(MathUtils.circleArea(5.0))
println(MathUtils.getCallCount)  // 1
println(MathUtils.getCallCount)  // 2

伴生对象

scala
// 类和其伴生对象可以互相访问私有成员
class BankAccount private (private var balance: Double) {
  
  def deposit(amount: Double): Unit = {
    balance += amount
  }
  
  def getBalance: Double = balance
  
  // 访问伴生对象的私有成员
  def getNextAccountNumber: String = BankAccount.nextAccountNumber()
}

object BankAccount {
  private var accountCounter = 0
  
  // 工厂方法
  def apply(initialBalance: Double): BankAccount = {
    new BankAccount(initialBalance)
  }
  
  def apply(): BankAccount = {
    new BankAccount(0.0)
  }
  
  private def nextAccountNumber(): String = {
    accountCounter += 1
    f"ACC$accountCounter%06d"
  }
  
  // 访问类的私有成员
  def transfer(from: BankAccount, to: BankAccount, amount: Double): Boolean = {
    if (from.balance >= amount) {
      from.balance -= amount
      to.balance += amount
      true
    } else {
      false
    }
  }
}

// 使用伴生对象
val account1 = BankAccount(1000.0)  // 使用 apply 方法
val account2 = BankAccount()        // 使用无参 apply 方法

account1.deposit(500.0)
BankAccount.transfer(account1, account2, 200.0)

println(account1.getBalance)  // 1300.0
println(account2.getBalance)  // 200.0

案例类(Case Class)

基本案例类

scala
// 案例类自动生成很多有用的方法
case class Person(name: String, age: Int)

val person1 = Person("Alice", 25)  // 不需要 new 关键字
val person2 = Person("Alice", 25)

// 自动生成的方法
println(person1)                    // Person(Alice,25) - toString
println(person1 == person2)         // true - equals
println(person1.hashCode)           // 自动生成的 hashCode

// 模式匹配支持
person1 match {
  case Person(name, age) => println(s"Name: $name, Age: $age")
}

// copy 方法
val person3 = person1.copy(age = 26)
println(person3)  // Person(Alice,26)

// 字段访问
println(person1.name)  // Alice
println(person1.age)   // 25

案例类的高级特性

scala
case class Address(street: String, city: String, zipCode: String)

case class Person(
  name: String, 
  age: Int, 
  email: Option[String] = None,
  address: Option[Address] = None
) {
  // 可以添加自定义方法
  def isAdult: Boolean = age >= 18
  
  def hasEmail: Boolean = email.isDefined
  
  def fullAddress: String = address match {
    case Some(addr) => s"${addr.street}, ${addr.city} ${addr.zipCode}"
    case None => "No address"
  }
}

val address = Address("123 Main St", "New York", "10001")
val person = Person(
  name = "John Doe",
  age = 30,
  email = Some("john@example.com"),
  address = Some(address)
)

println(person.isAdult)      // true
println(person.hasEmail)     // true
println(person.fullAddress)  // 123 Main St, New York 10001

// 嵌套 copy
val movedPerson = person.copy(
  address = person.address.map(_.copy(city = "Boston"))
)

继承

基本继承

scala
// 基类
class Animal(val name: String) {
  def speak(): String = "Some sound"
  
  def move(): String = "Moving"
  
  // 可以被重写的方法
  def describe(): String = s"This is $name"
}

// 派生类
class Dog(name: String, val breed: String) extends Animal(name) {
  
  // 重写方法
  override def speak(): String = "Woof!"
  
  override def describe(): String = s"This is $name, a $breed dog"
  
  // 新方法
  def wagTail(): String = s"$name is wagging tail"
}

class Cat(name: String) extends Animal(name) {
  override def speak(): String = "Meow!"
  
  def purr(): String = s"$name is purring"
}

// 使用继承
val dog = new Dog("Buddy", "Golden Retriever")
val cat = new Cat("Whiskers")

println(dog.speak())      // Woof!
println(dog.describe())   // This is Buddy, a Golden Retriever dog
println(dog.wagTail())    // Buddy is wagging tail

println(cat.speak())      // Meow!
println(cat.purr())       // Whiskers is purring

抽象类

scala
// 抽象类
abstract class Shape {
  // 抽象方法
  def area(): Double
  def perimeter(): Double
  
  // 具体方法
  def describe(): String = s"A shape with area ${area()} and perimeter ${perimeter()}"
}

class Rectangle(val width: Double, val height: Double) extends Shape {
  def area(): Double = width * height
  def perimeter(): Double = 2 * (width + height)
}

class Circle(val radius: Double) extends Shape {
  def area(): Double = math.Pi * radius * radius
  def perimeter(): Double = 2 * math.Pi * radius
}

val rectangle = new Rectangle(5.0, 3.0)
val circle = new Circle(2.0)

println(rectangle.describe())
println(circle.describe())

// 多态
val shapes: List[Shape] = List(rectangle, circle)
shapes.foreach(shape => println(s"Area: ${shape.area()}"))

特征(Trait)

基本特征

scala
// 特征定义
trait Drawable {
  def draw(): String
}

trait Movable {
  def move(x: Int, y: Int): String
}

// 类可以混入多个特征
class Circle(val radius: Double) extends Drawable with Movable {
  def draw(): String = s"Drawing a circle with radius $radius"
  
  def move(x: Int, y: Int): String = s"Moving circle to ($x, $y)"
}

class Rectangle(val width: Double, val height: Double) extends Drawable with Movable {
  def draw(): String = s"Drawing a rectangle ${width}x${height}"
  
  def move(x: Int, y: Int): String = s"Moving rectangle to ($x, $y)"
}

val circle = new Circle(5.0)
val rectangle = new Rectangle(10.0, 8.0)

println(circle.draw())
println(rectangle.move(10, 20))

带有默认实现的特征

scala
trait Logger {
  // 抽象方法
  def log(message: String): Unit
  
  // 具体方法
  def info(message: String): Unit = log(s"INFO: $message")
  def error(message: String): Unit = log(s"ERROR: $message")
  def debug(message: String): Unit = log(s"DEBUG: $message")
}

class ConsoleLogger extends Logger {
  def log(message: String): Unit = println(message)
}

class FileLogger(filename: String) extends Logger {
  def log(message: String): Unit = {
    // 简化的文件写入
    println(s"Writing to $filename: $message")
  }
}

val consoleLogger = new ConsoleLogger()
val fileLogger = new FileLogger("app.log")

consoleLogger.info("Application started")
fileLogger.error("Database connection failed")

访问修饰符

访问级别

scala
class AccessExample {
  // 公共字段(默认)
  val publicField = "public"
  
  // 私有字段
  private val privateField = "private"
  
  // 受保护字段
  protected val protectedField = "protected"
  
  // 包私有
  private[scala] val packagePrivateField = "package private"
  
  def publicMethod(): String = "public method"
  
  private def privateMethod(): String = "private method"
  
  protected def protectedMethod(): String = "protected method"
  
  // 只能在类内部访问私有成员
  def accessPrivate(): String = privateMethod()
}

class SubClass extends AccessExample {
  // 可以访问受保护成员
  def accessProtected(): String = protectedMethod()
  
  // 不能访问私有成员
  // def accessPrivate(): String = privateMethod()  // 编译错误
}

实际应用示例

银行账户系统

scala
// 基础账户类
abstract class Account(val accountNumber: String, protected var balance: Double) {
  
  def getBalance: Double = balance
  
  def deposit(amount: Double): Boolean = {
    if (amount > 0) {
      balance += amount
      true
    } else {
      false
    }
  }
  
  // 抽象方法,由子类实现
  def withdraw(amount: Double): Boolean
  
  def transfer(to: Account, amount: Double): Boolean = {
    if (withdraw(amount)) {
      to.deposit(amount)
      true
    } else {
      false
    }
  }
}

// 储蓄账户
class SavingsAccount(accountNumber: String, balance: Double, val interestRate: Double) 
  extends Account(accountNumber, balance) {
  
  def withdraw(amount: Double): Boolean = {
    if (amount > 0 && amount <= balance) {
      balance -= amount
      true
    } else {
      false
    }
  }
  
  def addInterest(): Unit = {
    balance += balance * interestRate
  }
}

// 支票账户
class CheckingAccount(accountNumber: String, balance: Double, val overdraftLimit: Double)
  extends Account(accountNumber, balance) {
  
  def withdraw(amount: Double): Boolean = {
    if (amount > 0 && amount <= balance + overdraftLimit) {
      balance -= amount
      true
    } else {
      false
    }
  }
  
  def getAvailableBalance: Double = balance + overdraftLimit
}

// 使用示例
val savings = new SavingsAccount("SAV001", 1000.0, 0.02)
val checking = new CheckingAccount("CHK001", 500.0, 200.0)

savings.deposit(500.0)
savings.addInterest()
println(s"Savings balance: ${savings.getBalance}")

checking.withdraw(600.0)  // 使用透支
println(s"Checking balance: ${checking.getBalance}")
println(s"Available balance: ${checking.getAvailableBalance}")

// 转账
savings.transfer(checking, 300.0)
println(s"After transfer - Savings: ${savings.getBalance}, Checking: ${checking.getBalance}")

图形系统

scala
// 特征定义
trait Drawable {
  def draw(): String
}

trait Scalable {
  def scale(factor: Double): Unit
}

trait Colorable {
  var color: String
  def setColor(newColor: String): Unit = color = newColor
}

// 抽象基类
abstract class Shape extends Drawable {
  def area(): Double
  def perimeter(): Double
}

// 具体实现
class Circle(var radius: Double, var color: String = "black") 
  extends Shape with Scalable with Colorable {
  
  def area(): Double = math.Pi * radius * radius
  
  def perimeter(): Double = 2 * math.Pi * radius
  
  def draw(): String = s"Drawing a $color circle with radius $radius"
  
  def scale(factor: Double): Unit = radius *= factor
}

class Rectangle(var width: Double, var height: Double, var color: String = "black")
  extends Shape with Scalable with Colorable {
  
  def area(): Double = width * height
  
  def perimeter(): Double = 2 * (width + height)
  
  def draw(): String = s"Drawing a $color rectangle ${width}x${height}"
  
  def scale(factor: Double): Unit = {
    width *= factor
    height *= factor
  }
}

// 使用示例
val shapes: List[Shape with Scalable with Colorable] = List(
  new Circle(5.0, "red"),
  new Rectangle(10.0, 8.0, "blue")
)

shapes.foreach { shape =>
  println(shape.draw())
  println(s"Area: ${shape.area()}")
  
  shape.scale(1.5)
  shape.setColor("green")
  
  println(s"After scaling and recoloring: ${shape.draw()}")
  println(s"New area: ${shape.area()}")
  println()
}

练习

练习 1:学生管理系统

scala
object StudentManagementSystem {
  
  case class Course(code: String, name: String, credits: Int)
  
  case class Grade(course: Course, score: Double) {
    def letterGrade: String = score match {
      case s if s >= 90 => "A"
      case s if s >= 80 => "B"
      case s if s >= 70 => "C"
      case s if s >= 60 => "D"
      case _ => "F"
    }
    
    def isPass: Boolean = score >= 60
  }
  
  class Student(val id: String, val name: String) {
    private var grades: List[Grade] = List()
    
    def addGrade(grade: Grade): Unit = {
      grades = grade :: grades
    }
    
    def getGrades: List[Grade] = grades.reverse
    
    def gpa: Double = {
      if (grades.isEmpty) 0.0
      else {
        val totalPoints = grades.map(g => g.score * g.course.credits).sum
        val totalCredits = grades.map(_.course.credits).sum
        totalPoints / totalCredits
      }
    }
    
    def totalCredits: Int = grades.filter(_.isPass).map(_.course.credits).sum
    
    override def toString: String = s"Student($id, $name, GPA: ${gpa})"
  }
  
  def main(args: Array[String]): Unit = {
    val math = Course("MATH101", "Calculus I", 4)
    val cs = Course("CS101", "Introduction to Programming", 3)
    val english = Course("ENG101", "English Composition", 3)
    
    val student = new Student("S001", "Alice Johnson")
    
    student.addGrade(Grade(math, 85.0))
    student.addGrade(Grade(cs, 92.0))
    student.addGrade(Grade(english, 78.0))
    
    println(student)
    println(s"Total Credits: ${student.totalCredits}")
    
    student.getGrades.foreach { grade =>
      println(s"${grade.course.name}: ${grade.score} (${grade.letterGrade})")
    }
  }
}

练习 2:电商系统

scala
object ECommerceSystem {
  
  trait Discountable {
    def applyDiscount(percentage: Double): Unit
  }
  
  abstract class Product(val id: String, val name: String, protected var price: Double) {
    def getPrice: Double = price
    override def toString: String = s"$name ($$${price})"
  }
  
  class Book(id: String, name: String, price: Double, val author: String, val isbn: String)
    extends Product(id, name, price) with Discountable {
    
    def applyDiscount(percentage: Double): Unit = {
      price = price * (1 - percentage / 100)
    }
    
    override def toString: String = s"Book: $name by $author ($$${price})"
  }
  
  class Electronics(id: String, name: String, price: Double, val brand: String, val warranty: Int)
    extends Product(id, name, price) with Discountable {
    
    def applyDiscount(percentage: Double): Unit = {
      price = price * (1 - percentage / 100)
    }
    
    override def toString: String = s"Electronics: $name by $brand ($$${price}, ${warranty}y warranty)"
  }
  
  case class CartItem(product: Product, quantity: Int) {
    def totalPrice: Double = product.getPrice * quantity
  }
  
  class ShoppingCart {
    private var items: List[CartItem] = List()
    
    def addItem(product: Product, quantity: Int): Unit = {
      items = CartItem(product, quantity) :: items
    }
    
    def removeItem(productId: String): Unit = {
      items = items.filterNot(_.product.id == productId)
    }
    
    def getItems: List[CartItem] = items.reverse
    
    def totalAmount: Double = items.map(_.totalPrice).sum
    
    def applyBulkDiscount(percentage: Double): Unit = {
      items.foreach { item =>
        item.product match {
          case discountable: Discountable => discountable.applyDiscount(percentage)
          case _ => // 不支持折扣的产品
        }
      }
    }
  }
  
  def main(args: Array[String]): Unit = {
    val book = new Book("B001", "Scala Programming", 45.99, "Martin Odersky", "978-0981531687")
    val laptop = new Electronics("E001", "MacBook Pro", 1299.99, "Apple", 1)
    
    val cart = new ShoppingCart()
    cart.addItem(book, 2)
    cart.addItem(laptop, 1)
    
    println("Shopping Cart:")
    cart.getItems.foreach(item => 
      println(s"${item.product} x ${item.quantity} = $$${item.totalPrice}")
    )
    println(s"Total: $$${cart.totalAmount}")
    
    println("\nApplying 10% bulk discount...")
    cart.applyBulkDiscount(10.0)
    
    println("Updated Cart:")
    cart.getItems.foreach(item => 
      println(s"${item.product} x ${item.quantity} = $$${item.totalPrice}")
    )
    println(s"New Total: $$${cart.totalAmount}")
  }
}

总结

本章详细介绍了 Scala 中类和对象的核心概念:

  • 类定义:构造函数、字段、方法的定义
  • 对象:单例对象和伴生对象的使用
  • 案例类:自动生成有用方法的特殊类
  • 继承:类的继承和方法重写
  • 特征:多重继承和混入机制
  • 访问修饰符:控制成员的可见性
  • 实际应用:银行系统、图形系统等实例

掌握这些面向对象编程概念是构建复杂 Scala 应用的基础。在下一章中,我们将学习 Scala Trait 特征,深入了解 Scala 独特的特征系统和混入机制。

本站内容仅供学习和研究使用。