paulmdavies
paulmdavies

Reputation: 1288

Scala case class unchangeable member

In Scala, I have a case class representing some data:

case class Foo( val start : Int, val end : Int )

and I often use the copy method.

However, I've now reached the point where I'd like to add an extra member as an identifier, because I want the following equality behaviour:

case class NewFoo( val id : Int, val start : Int, val end : Int )
{
    override def equals( any : Any ) = any match
    {
        case other : NewFoo => id == other.id
        case _ => false
    }
}

However, I'd now like to prevent the user changing the id when they copy a NewFoo instance, thus, once a NewFoo is created, its data can change, but its id will always remain the same.

Is this possible with case classes? I suspect not, since it's not really what they're designed for. On the other hand, a definition I've read of case classes is that they are "plain and immutable data-holding objects that should exclusively depend on their constructor arguments", a definition which doesn't seem to be broken by my use case.

If a case class is inappropriate, is there a way I can replicate the functionality of a one, particularly the copy method, without having an explicit case class? I'm not using pattern matching.

Upvotes: 0

Views: 110

Answers (1)

senia
senia

Reputation: 38045

Yes, you could implement copy method manually like this:

sealed class NewFoo(val id: Int, val start: Int, val end: Int) {
  def copy(start: Int = start, end: Int = end) = new NewFoo(id, start, end)

  override def equals(any: Any) = any match {
    case other : NewFoo => id == other.id
    case _ => false
  }

  // don't forget to override `hashCode` with `equals`
  override def hashCode() = id.hashCode()
}

Note that you should also override hashCode with equals.

You could also make your class sealed to avoid problems with this equals implementation.

See also Programming in Scala/Object Equality.

You could also save pattern matching and instance creation without new (NewFoo(0, 0, 0)) with this companion object implementation:

object NewFoo {
  def apply(id: Int, start: Int, end: Int) = new NewFoo(id, start, end)
  def unapply(f: NewFoo): Option[(Int, Int, Int)] = Some((f.id, f.start, f.end))
}

Upvotes: 4

Related Questions