Scala中带协变参数的case class与无类型参数基类的映射转换方法
实现方案
首先注意你现有代码存在命名大小写不一致的问题:特质定义用了小写status,后续泛型约束用了大写Status,我们先统一调整为符合Scala命名规范的写法:
// 统一修正为大写开头的类型名 sealed trait Status extends Product with Serializable object Status{ case object Pending extends Status case object Ready extends Status type Ready = Ready.type type Pending = Pending.type } case class Container[+S <: Status](status : S, commonValue: String) case class NotAContainer(status : Status, commonValue:String)
方案1:基于sealed特质的穷尽模式匹配转换
因为Status是sealed特质,编译器可以自动校验匹配是否穷尽,直接对NotAContainer的status字段做匹配即可生成对应泛型类型的Container实例:
def toContainer(nac: NotAContainer): Container[_ <: Status] = nac.status match { case s @ Status.Ready => Container(s, nac.commonValue) case s @ Status.Pending => Container(s, nac.commonValue) }
如果需要拿到具体的Container[Ready]或者Container[Pending]精确类型,在使用端对返回值再做一次模式匹配即可。
方案2:给NotAContainer增加泛型参数(调整status字段类型的最优方案)
如果你允许修改NotAContainer的定义,直接给它加上和Container一致的泛型参数,就可以零成本直接转换,无需运行时匹配:
// 调整后的NotAContainer定义 case class NotAContainer[+S <: Status](status : S, commonValue:String) // 直接转换即可,编译期保证类型安全 def toContainer[S <: Status](nac: NotAContainer[S]): Container[S] = Container(nac.status, nac.commonValue)
方案3:类型类实现隐式转换(适合频繁扩展枚举的场景)
如果后续要新增Status的枚举值,不想每次修改匹配逻辑,可以用类型类实现转换逻辑:
trait ToContainer[S <: Status] { def convert(status: S, commonValue: String): Container[S] } object ToContainer { implicit val readyToContainer: ToContainer[Status.Ready] = (status, commonValue) => Container(status, commonValue) implicit val pendingToContainer: ToContainer[Status.Pending] = (status, commonValue) => Container(status, commonValue) } // 转换方法 def toContainer[S <: Status](nac: NotAContainer[S])(implicit tc: ToContainer[S]): Container[S] = tc.convert(nac.status, nac.commonValue)
内容的提问来源于stack exchange,提问作者athomassi
相关产品推荐
相关产品推荐

