题目
实现一个不可变的Person类并解释设计选择
信息
- 类型:问答
- 难度:⭐⭐
考点
不可变类设计,Scala类定义,伴生对象应用,val与var的区别
快速回答
实现要点:
- 使用
class Person(val name: String, val age: Int)定义不可变类 - 实现方法
def isAdult: Boolean = age >= 18 - 通过伴生对象提供工厂方法
- 不可变设计的优点:线程安全、易于推理、适合函数式编程
原理说明
在Scala中,不可变对象一旦创建,其状态就不能改变。这带来了诸多好处:
- 线程安全:无需同步即可在多线程环境中共享
- 易于推理:对象状态在创建后固定不变
- 函数式编程友好:支持纯函数和无副作用编程
- 安全的哈希键:可用作HashMap键而不会因状态改变导致问题
代码实现
// 主类定义
class Person(val name: String, val age: Int) {
// 判断是否成年
def isAdult: Boolean = age >= 18
// 创建新实例的方法(保持不可变性)
def withName(newName: String): Person = new Person(newName, age)
def withAge(newAge: Int): Person = new Person(name, newAge)
}
// 伴生对象
object Person {
// 工厂方法
def apply(name: String, age: Int): Person = new Person(name, age)
// 提取器用于模式匹配
def unapply(p: Person): Option[(String, Int)] = Some((p.name, p.age))
}最佳实践
- 所有字段声明为
val确保不可变性 - 提供
withXxx方法返回新实例而非修改状态 - 使用伴生对象:
- 实现
apply工厂方法(支持Person("Alice", 30)创建) - 实现
unapply支持模式匹配
- 实现
- 对于复杂对象,考虑使用
case class(自动生成以上功能)
常见错误
- 错误1:使用
var定义字段// 错误示例 class Person(var name: String, var age: Int) // 可变状态! - 错误2:暴露可变集合
应改为不可变集合:// 错误示例 class Person(val hobbies: scala.collection.mutable.ListBuffer[String])val hobbies: List[String] - 错误3:修改方法返回原实例
// 错误示例 def setAge(newAge: Int): Unit = { this.age = newAge }
扩展知识
- case class替代方案:
自动获得:case class Person(name: String, age: Int) { def isAdult: Boolean = age >= 18 }- 不可变字段
- apply/unapply方法
- equals/hashCode/toString实现
- copy方法(替代withXxx)
- 防御性拷贝:当包含可变对象时,应在构造函数和getter中进行拷贝
- 性能考量:频繁创建新对象可能影响性能,可考虑结构共享技术