Reputation: 6083
I am using case classes for models in an ORM. Each model has an id, but the id shouldn't be publicly accessible. So I have a parent trait
trait WithId {
private var id: Long = 0
}
and a lot of case classes (the models) inheriting from it
case class C1(a: Int, b: String) extends WithId
case class C2(...) extends WithId
...
Now, if someone calls copy() on a case class, it doesn't copy the id with it, but sets it to 0.
val c1 = C1(3, "bla")
//Set c1.id to a value != 0
val c2 = c1.copy(b="bla2")
//c1.id !=0, but c2.id = 0
I want it to copy the id as well.
Since I have a lot of these case classes, I'd prefer to have as few code as possible in the case classes themselves. So implementing a copy() method in each case class would be a lot of boilerplate code.
Is there a way to implement something in the trait that makes copy() also copy the id? Maybe something using macros? Or is there another way I didn't think about at all?
edit:
I could override the id field in each case class like
case class C1(a: Int, b: String, protected var id: Long)
Then it would be copied. But that also is boilerplate code I have to write per case class and it's hard to explain why you have to add an id field to a case class although you never notice it or can use it anywhere else when using the case class. I'd like to avoid that if possible.
Upvotes: 3
Views: 1399
Reputation: 167891
If I were you, I'd add an ID-carrying token:
class IdToken private[myPackage] (val id: Int) {
override def equals(a: Any) = a match {
case tok: IdToken => id == tok.id
case _ => false
}
override def hashCode = id.hashCode ^ 0x157135
override def toString = ""
}
object IdToken {
private var lastId = 0
private[myPackage] def next = { lastId += 1; new IdToken(lastId) }
}
Since the constructor is private to your package, nobody else but you can create these tokens.
Then you write your trait as
trait WithID { protected def idToken: IdToken }
and
case class C1(a: Int, b: String, protected val idToken: IdToken = IdToken.next) extends WithID {
def checkId = idToken.id
}
(where you only need checkId
for the test below) so you can
scala> C1(5, "fish")
res1: C1 = C1(5,fish,)
scala> res1.copy(a = 3)
res2: C1 = C1(3,fish,)
scala> res1.checkId == res2.checkId
res3: Boolean = true
But you can't access the token from outside code because the val
is protected
.
Hopefully that is good enough encapsulation.
Upvotes: 1