在Scala中,抽象类常用于定义一组具有共性的属性和行为,但实际开发中我们常面临对象属性修改以及基于已有对象生成新对象的需求。传统面向对象写法多用可变变量,而Scala提供的不可变模式能让这类操作更清晰安全。

可变状态下的抽象类实现
先来看一个使用抽象类与可变属性的常见写法,通过直接修改属性或手动新建对象来模拟克隆:
abstract class Shape {
var color: String
def describe: String
}
class Circle(var radius: Double, var color: String) extends Shape {
def describe: String = s"Circle radius=$radius color=$color"
}
// 修改属性
val c1 = new Circle(1.0, "red")
c1.radius = 2.0
// 简单克隆并改色
val c2 = new Circle(c1.radius, "blue")
这种写法在单线程下可用,但在并发或复杂逻辑中,可变状态会导致难以追踪的 bug。
使用不可变模式优雅实现
Scala更推荐用不可变字段与 case class 配合抽象类,利用 copy 方法完成属性修改与克隆:
abstract class Shape {
def color: String
def describe: String
}
case class Circle(radius: Double, color: String) extends Shape {
def describe: String = s"Circle radius=$radius color=$color"
}
val c1 = Circle(1.0, "red")
// 修改半径,生成新对象而非改原对象
val c2 = c1.copy(radius = 2.0)
// 克隆并改颜色
val c3 = c1.copy(color = "blue")
为什么这样更优雅
- 原对象始终不变,避免副作用
- copy 方法语义明确,代码可读性强
- 天然适合多线程与函数式编程
抽象类与不可变模式的结合建议
若必须在抽象类中定义行为,可将状态放在具体的 case class 里。如下示例展示抽象类定义接口,case class 承载不可变数据:
abstract class Animal {
def name: String
def sound: String
def withName(newName: String): Animal
}
case class Dog(name: String, sound: String) extends Animal {
def withName(newName: String): Dog = this.copy(name = newName)
}
val d1 = Dog("Tom", "wang")
val d2 = d1.withName("Jerry")
通过 withName 这类方法,我们既保留了抽象类的抽象能力,又让对象修改与克隆保持在不可变框架内,整体设计更稳健。