开发者

Using Sub-Types And Return Types in Scala to Process a Generic Object Into a Specific One

I think this is about covariance but I'm weak on the topic...

I have a generic Event class used for things like database persistance, let's say like this:

class Event(
  subject: Long,
  verb: String,
  directobject: Option[Long],
  indirectobject: Option[Long],
  timestamp: Long)
{
  def getSubject = subject
  def getVerb = verb
  def getDirectObject = directobject
  def getIndirectObject = indirectobject
  def getTimestamp = timestamp
}

However, I have lots of different event verbs and I want to use pattern matching and such with these different event types, so I will create some corresponding case classes:

trait EventCC
case class Login(user: Long, timestamp: Long) extends EventCC
case class Follow(
  follower: Long,
  followee: Long,
  timestamp: Long
) extends EventCC

Now, the question is, how can I easily convert generic Events to the specific case classes.

This is my first stab at it:

def event2CC[T <: EventCC](event: Event): T = event.getVerb match {
  case "login" => Login(event.getSubject, event.getTimestamp)
  case "follow" => Follow(
    event.getSubject,
    event.getDirectObject.getOrElse(0),
    event.getTimestamp
  )
  // ...
}

Unfortunately, this is wrong.

<console>:11: error: type mismatch;
 found   : Login
 required: T
   开发者_高级运维          case "login" => Login(event.getSubject, event.getTimestamp)
                             ^
<console>:12: error: type mismatch;
 found   : Follow
 required: T
             case "follow" => Follow(event.getSubject, 
event.getDirectObject.getOrElse(0), event.getTimestamp)

Could someone with greater type-fu than me explain if, 1) if what I want to do is possible (or reasonable, for that matter), and 2) if so, how to fix event2CC. Thanks!


It seems to me that the best thing you can return is EventCC:

def event2CC(event: Event): EventCC

The type of T cannot be made more specific at compile time. It's only at run-time that we know if T is precisely Login or Follow and this only depends on event values.


If you want to be able to use Events in pattern matching, you could define an extractor for them:

object Event {
  def unapply(evt: Event): Some((Long, String, Option[Long])) = 
    Some(evt.getSubject, evt.getVerb, evt.getDirectObject)
}

val evt: Event = retrieveEventFromEther()
evt match {
  case Event(_, "login", _) => "It was a login!"
  case Event(_, "follow", Some(_)) => "It was a follow with a direct object!"
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜