Joe
Joe

Reputation: 1723

Scala: Copying case classes with trait

I'm fairly new to Scala and I have a question about the best way to copy a case class while preserving data that comes from traits. For example, let's say I have the following:

trait Auditing {

  var createTime: Timestamp = new Timestamp(System.currentTimeMillis)
}

case class User(val userName: String, val email: String) extends Auditing

val user = User("Joe", "[email protected]")

Then I want to make a new copy with one parameter changed:

val user2 = user.copy(email = "[email protected]")

Now, in the example above, the property createTime does not get copied over because it is not defined in the constructor of the User case class. So my question is: assuming that moving createTime into the constructor is not an option, what is the best way for getting a copy of the User object that includes the value from the trait?

I'm using Scala 2.9.1

Thanks in advance! Joe

Upvotes: 10

Views: 4490

Answers (3)

Joerg Schmuecker
Joerg Schmuecker

Reputation: 111

You might be better of not using a case class. You can easily implement the functionality you need yourself. The below code implements the copy method you wanted, a constructor without new, hides the original constructor, and creates an extractor so that you can use User in case statements.

class User private(val userName: String,
                   val email: String,
                   val timeStamp: Timestamp =
                   new Timestamp(System.currentTimeMillis)) {

    def copy(uName: String = userName,
             eMail: String = email) =
    new User(uName, eMail, timeStamp)
}

object User {
  def apply(userName: String, email: String) =
    new User(userName, email)

  def unapply(u: User) = Some((u.userName, u.email, u.timeStamp))
}

Upvotes: 0

Landei
Landei

Reputation: 54584

While I see no other solution than Reuben's, I don't understand the requirement to leave the constructor args untouched. This would be the most natural solution:

case class User(userName: String, email: String, 
   override val createTime:Timestamp = new Timestamp(System.currentTimeMillis)) 
      extends Auditing

If you don't want the user to be able to overwrite createTime, you can still use:

case class User private (userName: String, email: String, 
   override val createTime:Timestamp) extends Auditing {
   def this(userName: String, email: String) =
     this(userName, email, new Timestamp(System.currentTimeMillis))
}

The only drawback is that you need to write new User("Joe", "[email protected]"), as the primary constructor is now private.

Upvotes: 3

Reuben Doetsch
Reuben Doetsch

Reputation: 616

You can override the copy method with that behavior.

case class User(val userName: String, val email: String) extends Auditing
{
  def copy(userName = this.userName, email = this.email) {
   val copiedUser = User(userName, email)
   copiedUser.createTime = createTime
   copiedUser      
  }
}

Upvotes: 6

Related Questions