第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 val2.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 的作用是什么?