大家!
我对Scala模式匹配有一点问题。我在我的网络应用程序中使用Korolev框架,但我敢打赌Scala中的问题更深入。
我有所有州的基本特征:
trait BaseState {
val notifications: List[Notification]
}
两个派生的案例类:
case class GuestState(
notifications: List[Notification] = List(),
isRegistration: Boolean = false
) extends BaseState
和
case class AuthenticatedState(
notifications: List[Notification] = List(),
user: UserWithPermissions
) extends BaseState
在一个事件处理程序中,我需要在没有特定通知的情况下获得类似状态。现在它的工作原理如下:
event('click) { access =>
access.transition {
case s: GuestState => s.copy(notifications = notifications.filter(x => x != n))
case s: AuthenticatedState => s.copy(notifications = notifications.filter(x => x != n))
}
}
对于这两种类型,我必须完全复制代码,因为BaseState
没有copy()
方法,编译器因错误而失败。
我怎样才能以scala方式正确地完成它?谢谢。
这两个案例实际上并没有做同样的事情,因为两个copy
方法有不同的签名。因此虽然它可能在文本上是相同的,但代码实际上在每种情况下都做了不同的事情。
这里的一个选择是向特征添加一个抽象的filterNotifications
方法并调用:
trait BaseState {
val notifications: List[Notification]
def filterNotifications(f: Notification => Boolean): BaseState
}
case class GuestState(
notifications: List[Notification] = List(),
isRegistration: Boolean = false
) extends BaseState {
def filterNotifications(f: Notification => Boolean): BaseState =
this.copy(notifications=notifications.filter(f))
}
case class AuthenticatedState(
notifications: List[Notification] = List(),
user: UserWithPermissions
) extends BaseState {
def filterNotifications(f: Notification => Boolean): BaseState =
this.copy(notifications=notifications.filter(f))
}
我过去有过类似的场景,但从未找到过一个非常优雅的解决方案,因为具有相同或相似签名的复制方法不能以多态方式轻松表达。
我通常在特质和案例类中加入一些管道,以便重复至少在一个地方:
trait BaseState {
type ThisType <: BaseState
val notifications: List[Notification]
def withNotifications(notifications: List[Notification]): ThisType
}
case class GuestState(notifications: List[Notification] = List(),
isRegistration: Boolean = false) extends BaseState {
type ThisType = GuestState
def withNotifications(notifications: List[Notification]): ThisType =
copy(notifications = notifications)
}
case class AuthenticatedState(notifications: List[Notification] = List(),
user: UserWithPermissions) extends BaseState {
type ThisType = AuthenticatedState
def withNotifications(notifications: List[Notification]): ThisType =
copy(notifications = notifications)
}
这远非理想,也许有更好的模式。但是如果你像你的例子中那样使用逻辑很多,它至少会显着减少样板。如果还重复使用过滤器逻辑,则可以向基本特征添加更具体的filterNotififications
。
与其他答案相比,此解决方案的优势在于您在过滤时不会丢失类型精度。
并记住尽可能密封特性。
如果不改变基础特性,它是不可解决的。当编译器显然没有它时,编译器不允许你对特性执行操作copy
。
一种方法是在BaseState上指定映射/过滤功能,如其他答案所示。
另一种方法是用组合替换继承:
case class Notified(state: BaseState, notifications: List[Notification] = List())
sealed trait BaseState
case class GuestState(isRegistration: Boolean = false) extends BaseState
case class AuthenticatedState(user: UserWithPermissions) extends BaseState
val notified = Notified(GuestState())
val result = notified.copy(notifications = notifications.filter(_ => true))