为何两段Scala类初始化时的异常抛出行为存在差异?
两段代码异常抛出时机差异的核心原因
1. 第一段代码初始化即抛异常的逻辑
Scala类的普通val成员会在类实例初始化阶段同步完成赋值表达式的计算:
- 第一段代码中
att的赋值表达式本身就是throw new Exception(""),执行new Test()初始化实例时会直接运行该表达式,所以会立即抛出异常。
2. 第二段代码访问属性才抛异常的逻辑
差异的核心是Scala标准库中Map.mapValues的惰性求值特性:
mapValues方法默认返回的是惰性视图类型,不会立即执行传入的转换逻辑,只有在实际访问Map中存储的value时,才会触发转换函数的计算。
对应代码的执行流程:
- 你传入
mapValues的throw new Exception("")会被自动适配为忽略输入参数的转换函数_ => throw new Exception(""),完全符合mapValues要求的函数签名。 - 类初始化阶段只会完成
Seq((1, "a")).groupBy(_._1)的计算,mapValues仅会包装生成一个惰性视图对象,不会执行你传入的抛异常的转换逻辑,所以new Test()不会触发异常。 - 只有当你访问
att属性的具体值时,惰性视图才会触发转换函数的执行,抛出异常。
简易验证
将mapValues替换为严格求值的map实现,初始化阶段就会直接抛异常:
class Test { val att = Seq((1, "a")).groupBy(_._1).map { case (k, v) => k -> throw new Exception("") } } new Test() // 初始化直接抛出异常
内容的提问来源于stack exchange,提问作者Prassi
相关产品推荐
相关产品推荐

