Scala 2中如何恢复通配符的类型边界约束?
问题描述
以下代码在Scala 3中可以正常编译运行,但在Scala 2中编译失败:
trait Aggregate[T <: Aggregate[T]] { type B = T def split(percentage: Double): (T, T) def add(other: T): T } case class Scoria(total: Int) extends Aggregate[Scoria] { override def split(percentage: Double): (Scoria, Scoria) = { val amount = (total * (percentage / 100)).toInt (Scoria(total - amount), Scoria(amount)) } override def add(other: Scoria): Scoria = copy(total = total + other.total) } trait Transporter[T <: Aggregate[T]] { def load(aggregate: T): Transporter[T] } case class Truck[T <: Aggregate[T]](aggregate: T) extends Transporter[T] { override def load(aggregate: T): Transporter[T] = Truck(this.aggregate.add(aggregate)) } case class SiteProps[T <: Aggregate[T]](aggregate: T, transporter: Transporter[T]) { type A = T type C = T#B } trait Site { val props: SiteProps[_] private def runWith[T <: Aggregate[T]](props: SiteProps[T]): Unit = { val (remaining, going) = props.aggregate.split(50D) val loadedTransporter = props.transporter.load(going) println(loadedTransporter) } def run(): Unit = { runWith(props) } } val site = new Site { override val props: SiteProps[Scoria] = SiteProps(Scoria(3), Truck[Scoria](Scoria(0))) } site.run()
Scala 2中的编译报错信息:
runWith(props) ^ <pastie>:49: error: inferred type arguments [_$1] do not conform to method runWith's type parameter bounds [T <: Aggregate[T]] runWith(props) ^ <pastie>:49: error: type mismatch; found : SiteProps[_$1] where type _$1 required: SiteProps[T]
我尝试了以下强制类型转换写法,但都无效:
runWith(props.asInstanceOf[SiteProps[props.A]]) runWith(props.asInstanceOf[SiteProps[props.A#B]]) runWith(props.asInstanceOf[SiteProps[props.C]])
请问有没有办法让这段代码在Scala 2中编译通过(允许使用强制类型转换)?
解决方案
方案1:使用正确的强制类型转换写法
Scala 2无法直接通过props.A这类路径依赖类型完成转换,需要明确断言通配符类型满足Aggregate的自类型约束,修改run()方法如下:
def run(): Unit = { runWith(props.asInstanceOf[SiteProps[T forSome { type T <: Aggregate[T] }]]) }
或者用模式匹配给通配符类型命名后转换:
def run(): Unit = { props match { case p: SiteProps[t] => runWith(p.asInstanceOf[SiteProps[t <: Aggregate[t]]]) } }
方案2:调整Site的类型定义(无强制转换,更安全)
如果不想依赖强制转换,可以给Site添加抽象类型成员,让类型约束在编译期明确:
trait Site { type T <: Aggregate[T] val props: SiteProps[T] private def runWith(props: SiteProps[T]): Unit = { val (remaining, going) = props.aggregate.split(50D) val loadedTransporter = props.transporter.load(going) println(loadedTransporter) } def run(): Unit = { runWith(props) } } val site = new Site { type T = Scoria override val props: SiteProps[Scoria] = SiteProps(Scoria(3), Truck[Scoria](Scoria(0))) }
这种写法完全符合Scala 2的类型系统规则,不需要任何强制转换,是更推荐的实现方式。
内容的提问来源于stack exchange,提问作者steinybot
相关产品推荐
相关产品推荐

