简体   繁体   中英

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

With the trait:

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

Produces this error:

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$1: String): Unit = ???
[error]   def teamId_=(x$1: String): Unit = ???
[error]   case class CaseClassJobEvent(
[error]              ^

What am I doing wrong? What should I be doing? Is there anyway to use traits or inheritance to enforce properties on a case class? I don't want to enforce methods just properties.

Luis Miguel Mejía Suárez already answered your question, so I thought I would add something here so you might reconsider the design of your class.

Instead of creating a var in your case class, use the .copy method for case class to generate a new object with the changed fields.

So, if you want to change teamId or actorId in your case class do something like this:

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

To answer your original question:

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

A design like this is probably what you wanted. It's fine for a school project, but you would never do something like this in production, especially if you are working in a multithreaded environment. Immutable values are always thread-safe, and you would be breaking that by adding vars to your class.

Just something to consider.

=======

We are currently discussing how to implement CaseClassJobEvent without using mutable values. Here is my suggested implementation.

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

Everything is the same as the solution you want except teamId and actorId are not vars.

If you need to change the value of teamId and actorId in your case class to something else, do something like this:

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
}

If this seems like a horribly verbose way to have to modify a case class, you're right. What we currently do at our company is use the quicklens library from softwaremill to modify deeply nested case classes with more elegant syntax. It is still not as simple as reassigning a var, but it is more correct in a multithreaded environment, and it is less verbose than calling copy all the time. However, for your purposes, if you want correctness without learning a new library, copy may be your best bet.

=======

The way the conversation is evolving now is that the person who asked the question just wants to have an input that will do something if it has certain fields. This sounds like a job for inheritance.

Suppose I have lots of events.

trait Event

I want to have a function that only does something if my event has actorId and teamId.

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

Here is my function, that only does something if my event is an IdEvent

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

where someProcess has signature

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM