2Scala for the Impatient(第2版)

面向对象

Scala的OOP

类与对象trait抽象类case类

🎯 AI 时代的学习建议:不要只读理论。理解这一章的设计动机(为什么需要这个概念?)和边界条件(什么情况下会失效?)。当 AI 生成的代码涉及这些概念时,你能判断它是否用对了场景。AI 能帮你总结内容,但无法替代你亲手阅读、思考、建立直觉的过程。

第2章:面向对象

一、导读

1.1 本章学习目标

第2章讲解 Scala 的面向对象特性:类与对象、trait、抽象类、case 类。

本章核心学习目标:

  • 掌握 Scala 的类定义:构造器、字段、方法
  • 理解 trait:Scala 的多重继承
  • 掌握 case 类:不可变数据类
  • 理解 object 和伴生对象

1.2 为什么本章如此重要

Scala 的 OOP 比 Java 更强大。trait 和 case 类是 Scala 的核心特性。

1.3 核心问题引导

Scala 的 trait 和 Java 的 interface 有什么区别?

case 类比普通类多了什么?

object 和 class 的关系是什么?


二、核心概念详解

2.1 类和对象

scala
class Person(val name: String, var age: Int) {
    // 主构造器参数直接成为字段
    
    // 辅助构造器
    def this(name: String) = this(name, 0)
    
    // 方法
    def greet: String = s"Hello, I'm $name, age $age"
    
    override def toString: String = s"Person($name, $age)"
}

val p = new Person("Alice", 30)
println(p.greet)  // Hello, I'm Alice, age 30
p.age = 31  // OK, age is var
// p.name = "Bob"  // Error, name is val

2.2 Trait

scala
// Trait: 可以包含抽象方法和具体方法
trait Drawable {
    def draw(): Unit
    def describe: String = "drawable"  // 具体方法
}

trait Resizable {
    def resize(factor: Double): Unit
}

// 类可以混入多个 trait
class Rectangle(var width: Double, var height: Double) 
    extends Drawable with Resizable {
    
    def draw(): Unit = println(s"Drawing rectangle ${width}x${height}")
    
    def resize(factor: Double): Unit = {
        width *= factor
        height *= factor
    }
    
    override def describe: String = s"Rectangle(${width}x${height})"
}

val rect = new Rectangle(10, 20)
rect.draw()
rect.resize(2.0)
println(rect.describe)  // Rectangle(20.0x40.0)

2.3 Case 类

scala
// Case 类: 不可变数据类
case class Point(x: Double, y: Double) {
    def distanceTo(other: Point): Double = {
        val dx = x - other.x
        val dy = y - other.y
        math.sqrt(dx * dx + dy * dy)
    }
}

// 自动生成: toString, equals, hashCode, copy
val p1 = Point(1, 2)
val p2 = Point(1, 2)
println(p1 == p2)  // true (结构相等)
println(p1)        // Point(1.0,2.0)

val p3 = p1.copy(x = 3)  // Point(3.0,2.0)

// 模式匹配
def describe(point: Point): String = point match {
    case Point(0, 0) => "Origin"
    case Point(x, 0) => s"On X axis at $x"
    case Point(0, y) => s"On Y axis at $y"
    case Point(x, y) => s"At ($x, $y)"
}

2.4 Object 和伴生对象

scala
class Circle(val radius: Double) {
    def area: Double = Circle.PI * radius * radius
}

object Circle {
    val PI = 3.14159265358979
    
    def apply(radius: Double): Circle = new Circle(radius)
    
    def unitCircle: Circle = Circle(1.0)
}

// 使用
val c = Circle(5)  // 调用 Circle.apply
val unit = Circle.unitCircle
println(c.area)

三、实战案例

3.1 案例一:表达式计算器

scala
sealed trait Expr
case class Number(value: Double) extends Expr
case class Add(left: Expr, right: Expr) extends Expr
case class Mul(left: Expr, right: Expr) extends Expr

def eval(expr: Expr): Double = expr match {
    case Number(v) => v
    case Add(l, r) => eval(l) + eval(r)
    case Mul(l, r) => eval(l) * eval(r)
}

def show(expr: Expr): String = expr match {
    case Number(v) => v.toString
    case Add(l, r) => s"(${show(l)} + ${show(r)})"
    case Mul(l, r) => s"(${show(l)} * ${show(r)})"
}

val expr = Add(Mul(Number(2), Number(3)), Number(4))
println(show(expr))  // ((2.0 * 3.0) + 4.0)
println(eval(expr))  // 10.0

四、本章小结

Trait 支持多重继承,可以包含抽象和具体方法

Case 类 自动生成 toString、equals、copy 等

Object 是单例,常作为伴生对象提供工厂方法


五、思考与练习

为什么 case 类的参数默认是 val?

sealed trait 的作用是什么?


2/7面向对象
0%