如何解决如何缩小状态机编码中的上限类型参数的类型?
sealed trait State extends Product with Serializable
object State {
final case object Raw extends State
final case class JustRight(temperature: Int) extends State
final case class Burnt(charCoalContent: Double) extends State
}
final case class Cake[S <: State](name: String,state: S)
这很好,因为现在我可以确保只尝试将Raw
个蛋糕放进烤箱,而不是立即吃掉它们。
但是有时我只是躺在Cake[State]
周围,想尝试食用它,但前提是它处于可食用状态。我当然可以总是在cake.state
上进行模式匹配,但是我认为应该可以通过添加方法def narrow[S <: State]: Cake[State] => Option[Cake[S]]
来节省一些击键。
但是,现在我正在努力实现该功能。编译器接受Try(cake.asInstanceOf[Cake[S]]).toOption
,但似乎总会成功(我猜是因为类型参数被擦除了,实际上任何类型的A
都会在这里被接受,而不仅仅是S
)。似乎有效的方法是Try(cake.copy(state = cake.state.asInstanceOf[S])).toOption
,但是现在我已经多余地复制了数据。还有另一种更好的方法吗?还是整个编码可能从一开始就存在缺陷?
解决方法
您可以使用 typeclass 解决此问题,该类型类检查并强制转换(以类型安全的方式)状态的类型。
sealed trait State extends Product with Serializable
object State {
final case object Raw extends State
type Raw = Raw.type
final case class JustRight(temperature: Int) extends State
final case class Burnt(charCoalContent: Double) extends State
sealed trait Checker[S <: State] {
def check(state: State): Option[S]
}
object Checker {
private def instance[S <: State](pf: PartialFunction[State,S]): Checker[S] =
new Checker[S] {
val f = pf.lift
override def check(state: State): Option[S] = f(state)
}
implicit final val RawChecker: Checker[Raw] = instance {
case Raw => Raw
}
implicit final val JustRightChecker: Checker[JustRight] = instance {
case s @ JustRight(_) => s
}
implicit final val BurntChecker: Checker[Burnt] = instance {
case s @ Burnt(_) => s
}
}
}
final case class Cake[S <: State](name: String,state: S)
def narrow[S <: State](cake: Cake[State])(implicit checker: State.Checker[S]): Option[Cake[S]] =
checker.check(cake.state).map(s => cake.copy(state = s))
您可以这样使用:
val rawCake: Cake[State] = Cake(name = "Foo",state = State.Raw)
narrow[State.Raw](rawCake)
// res: Option[Cake[State.Raw]] = Some(Cake(Foo,Raw))
narrow[State.JustRight](rawCake)
// res: Option[Cake[State.JustRight] = None
顺便说一句,如果您想避免使用copy
,则可以将check
更改为只返回 Boolean 并使用脏的asInstanceOf
。
// Technically speaking it is unsafe,but it seems to work just right.
def narrowUnsafe[S <: State](cake: Cake[State])(implicit checker: State.Checker[S]): Option[Cake[S]] =
if (checker.check(cake.state)) Some(cake.asInstanceOf[Cake[S]])
else None
(您可以看到运行here的代码)
,如果在编译时不知道Cake
的状态的特定类型,则可能要使用ClassTag
来检查状态的类型,因为抛出并捕获{ {1}}对我来说似乎不是一个好主意:
ClassCastException
这通过检查蛋糕的状态是否为def narrow[S <: State](cake: Cake[_])(implicit classTag: ClassTag[S]): Option[Cake[S]] =
Option.when(classTag.runtimeClass.isInstance(cake.state))(cake.asInstanceOf[Cake[S]])
的已擦除类的实例来工作。但是,如果您的州采用类型参数,则可能要改用S
。
但是,如果您确实知道特定类型,则可能要使用 TypeTag
=:=
进行检查(<:<
拒绝<:<
) :
Cake[State]
请注意,这些都不是很好的解决方案,我建议仅针对每种情况制作一个单独的方法,并使用纯模式匹配来获取答案。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。