如何在 Scala 中的案例 class 上强制执行具有特征的属性?

How to enforce properties with traits on a case class in scala?

case class CaseClassJobEvent(
                   jobId: String,
                   jobType: Option[String],
                   inPlanning: Option[Boolean],

                   teamId: String,
                   actorId: String,
                   adminActorId: Option[String],
                   sessionId: String,
                   clientSessionId: Option[String],
                   clientCreatedAt: Long,
                   seqId: Long,
                   isSideEffect: Option[Boolean],

                   opAction: String,
                   stepId: Option[String],
                   jobBaseStepId: Option[String],
                   fieldId: Option[String],

                   serverReceivedAt: Option[Long]) extends Event

具有特征:

trait Event {
  var teamId: String
  var actorId: String
}

产生此错误:

class CaseClassJobEvent needs to be abstract, since:
[error] it has 2 unimplemented members.
[error] /** As seen from class CaseClassJobEvent, the missing signatures are as follows.
[error]  *  For convenience, these are usable as stub implementations.
[error]  */
[error]   def actorId_=(x: String): Unit = ???
[error]   def teamId_=(x: String): Unit = ???
[error]   case class CaseClassJobEvent(
[error]              ^

我做错了什么?我应该做什么?无论如何使用特征或继承来强制案例 class 的属性?我不想强制执行方法只是属性。

Luis Miguel Mejía Suárez 已经回答了你的问题,所以我想我会在这里添加一些东西,这样你可能会重新考虑你的 class.

的设计

不要在您的案例 class 中创建 var,而是使用案例 class 的 .copy 方法来生成具有更改字段的新对象。

因此,如果您想更改您的案例中的 teamId 或 actorId class,请执行以下操作:

val jobEvent = CaseClassJobEvent(...)
val changedJobEvent = jobEvent.copy( teamId = "somenewvalue", actorId = "somenewvalue" )

回答你原来的问题:

trait Event {
  def teamId: String
  def actorId: String
}

case class CaseClassJobEvent(
                   jobId: String,
                   jobType: Option[String],
                   inPlanning: Option[Boolean],

                   var teamId: String,
                   var actorId: String,
                   adminActorId: Option[String],
                   sessionId: String,
                   clientSessionId: Option[String],
                   clientCreatedAt: Long,
                   seqId: Long,
                   isSideEffect: Option[Boolean],

                   opAction: String,
                   stepId: Option[String],
                   jobBaseStepId: Option[String],
                   fieldId: Option[String],

                   serverReceivedAt: Option[Long]) extends Event

这样的设计可能就是您想要的。这对于学校项目来说很好,但你永远不会在生产中做这样的事情,尤其是当你在多线程环境中工作时。不可变值始终是线程安全的,您可以通过向 class.

添加变量来打破它

只是需要考虑的事情。

=======

我们目前正在讨论如何在不使用可变值的情况下实现 CaseClassJobEvent。这是我建议的实现。

trait Event {
  def teamId: String
  def actorId: String
}

case class CaseClassJobEvent(
                   jobId: String,
                   jobType: Option[String],
                   inPlanning: Option[Boolean],

                   teamId: String,
                   actorId: String,
                   adminActorId: Option[String],
                   sessionId: String,
                   clientSessionId: Option[String],
                   clientCreatedAt: Long,
                   seqId: Long,
                   isSideEffect: Option[Boolean],

                   opAction: String,
                   stepId: Option[String],
                   jobBaseStepId: Option[String],
                   fieldId: Option[String],

                   serverReceivedAt: Option[Long]) extends Event

除teamId和actorId不是vars外,一切都和你想要的解决方案一样。

如果您需要将您的案例 class 中的 teamId 和 actorId 的值更改为其他值,请执行以下操作:

def setTeamIdAndActorId(myEvent: CaseClasJobEvent, newTeamId: Option[String], newActorId: Option[String]): CaseClassJobEvent = {
  val newEvent1 = if (newTeamId.isDefined) myEvent.copy(teamId = newTeamId.get) else myEvent
  val newEvent2 = if (newactorId.isDefined) newEvent1.copy(actorId = newActorId.get) else newEvent1
  newEvent2
}

如果这似乎是必须修改案例的一种非常冗长的方式 class,那么您是对的。我们公司目前所做的是使用 softwaremill 的 quicklens 库来修改深度嵌套的 case classes,语法更优雅。它仍然不像重新分配一个 var 那样简单,但是在多线程环境下它更正确,并且比一直调用 copy 更不冗长。但是,就您的目的而言,如果您想在不学习新库的情况下获得正确性,那么复制可能是您最好的选择。

=======

现在对话的发展方式是,提出问题的人只想得到一个输入,如果它有特定的字段,它就会做某事。这听起来像是继承的工作。

假设我有很多事件。

trait Event

我想要一个函数,它只在我的事件有 actorId 和 teamId 时才执行某些操作。

trait IdEvent {
  def teamId: String
  def actorId: String
} extends Event

这是我的函数,只有当我的事件是 IdEvent 时它才会执行某些操作

def processEvent(e: Event): Option[Int] = {
  e match {
    case event: IdEvent => someProcess(event)
    case event: Event => None
  }
}

其中 someProcess 有签名

def someProcess(input: IdEvent): Option[Int]