Scala 2.13实现输入类型决定输出类型的编译时检查函数
编译时约束输入输出类型的Scala 2.13实现方案
以下两种方案均可实现需求中的编译时检查,无需依赖外部库:
方案一:路径依赖类型 + 中间特质
通过新增中间特质调整Out层级,结合路径依赖类型直接约束输入输出的对应关系:
1. 调整类型层级
新增Out1OrOut11特质限定In1允许的输出范围,同时为In的子类定义路径依赖类型:
trait In { // 该输入类型允许的输出类型 type AllowedOut <: Out } trait Out // 新增中间特质,限定In1可返回的输出类型 trait Out1OrOut11 extends Out case class In1() extends In { type AllowedOut = Out1OrOut11 } case class In2() extends In { type AllowedOut = Out2 } case class Out1() extends Out1OrOut11 case class Out11() extends Out1OrOut11 case class Out2() extends Out
2. 实现约束函数
利用路径依赖类型作为返回值,确保编译时验证类型合法性:
def process[I <: In](in: I): in.AllowedOut = in match { case _: In1 => Out1().asInstanceOf[in.AllowedOut] case _: In2 => Out2().asInstanceOf[in.AllowedOut] }
验证效果
- 合法调用(编译通过):
val res1: Out1 = process(In1()) // 允许返回Out1 val res11: Out11 = process(In1()) // 允许返回Out11 val res2: Out2 = process(In2()) // 允许返回Out2 - 非法调用(编译报错):
// 编译错误:类型不匹配,无法将In1#AllowedOut转换为Out2 val invalid: Out2 = process(In1())
方案二:类型类实现
无需修改原有类型层级,通过类型类标记合法的输入输出组合:
1. 定义类型类
创建类型类AllowedIO,为合法的输入输出组合提供隐式实例:
trait In trait Out case class In1() extends In case class In2() extends In case class Out1() extends Out case class Out11() extends Out case class Out2() extends Out // 类型类:标记I类型输入允许返回O类型输出 trait AllowedIO[I <: In, O <: Out] // 注册合法的输入输出组合 object AllowedIO { implicit val in1ToOut1: AllowedIO[In1, Out1] = new AllowedIO[In1, Out1] {} implicit val in1ToOut11: AllowedIO[In1, Out11] = new AllowedIO[In1, Out11] {} implicit val in2ToOut2: AllowedIO[In2, Out2] = new AllowedIO[In2, Out2] {} }
2. 实现约束函数
通过隐式参数AllowedIO[I, O]限制返回类型,只有合法组合能通过编译:
import AllowedIO._ def process[I <: In, O <: Out](in: I)(implicit ev: AllowedIO[I, O]): O = in match { case _: In1 => // 可根据逻辑返回Out1或Out11,asInstanceOf因隐式证据保证类型安全 scala.util.Random.nextBoolean() match { case true => Out1().asInstanceOf[O] case false => Out11().asInstanceOf[O] } case _: In2 => Out2().asInstanceOf[O] }
验证效果
- 合法调用(编译通过):
val res1: Out1 = process(In1()) val res11: Out11 = process(In1()) val res2: Out2 = process(In2()) - 非法调用(编译报错):
// 编译错误:找不到AllowedIO[In1, Out2]的隐式值 val invalid: Out2 = process(In1())
方案选择
- 方案一适合可调整原有类型层级的场景,代码逻辑更直观;
- 方案二无需修改原有类型结构,灵活性更强。
内容的提问来源于stack exchange,提问作者Pizza eu
相关产品推荐
相关产品推荐

