2

I have normal Scala class I am wanting to refactor to become an immutable case class. As I'm needing the class to be well-behaved in Set operations, I want all the Scala compiler automatically generated methods provided on a case class. IOW, I am wanting to avoid having to write these various methods; equals, hashCode, toString, etc., as that is very error prone. And I am needing to do this for a raft of classes, so I need a general solution, not just a specific solution anomalous quick fix or hack.

Here's the class with which I am working:

class Node(val identity: String, childrenArg: List[Node], customNodeArg: CustomNode) {
  val children: List[Node] = childrenArg
  val customNode: CustomNode = customNodeArg
}

As you can see, the class's constructor has three parameters. The first one, identity, is a read-only property. The remaining two, childrenArg and customNodeArg, are just a normal method parameters; i.e. they are only present during the construction of the instance and then disappears altogether from the class instance (unless otherwise captured) upon execution completion of the class constructor.

My first naive attempt to convert this to an immutable case class was this (just removing val from the first parameter):

class Node(identity: String, childrenArg: List[Node], customNodeArg: CustomNode) {
  val children: List[Node] = childrenArg
  val customNode: CustomNode = customNodeArg
}

However, this resulted in the undesired effect of both the childrenArg and customNodeArg parameters now being elevated to become (read-only) properties (as opposed to leaving them as normal method parameters). And this had the further undesired effect of having them included in the compiler generated equals and hashCode implementations.

How do I mark the immutable case class's constructor parameters childrenArg and customNodeArg such that identity is the only read-only property of the case class?

Any guidance on this; answers, website discussion links, etc., are greatly appreciated.

chaotic3quilibrium
  • 5,661
  • 8
  • 53
  • 86
  • This is specifically about the Scala _case_ class, not the generic Scala class. IOW, this is NOT a duplicate of this other StackOverflow questions: http://stackoverflow.com/q/1889454/501113 – chaotic3quilibrium Mar 22 '16 at 18:58

2 Answers2

3

A second parameter list seems to do the trick:

scala> trait CustomNode
defined trait CustomNode

scala> case class Node(identity: String)(childrenArg: List[Node], customNodeArg: CustomNode)
defined class Node

scala> val n = Node("id")(Nil, null)
n: Node = Node(id)

scala> n.identity
res0: String = id

scala> n.getClass.getDeclaredFields.map(_.getName)
res1: Array[String] = Array(identity)
Jean-Philippe Pellet
  • 59,296
  • 21
  • 173
  • 234
  • That's EXACTLY the type of solution I was seeking! Tysvm! Scala is SO flexible, there are times I cannot see a solution. I knew about additional argument lists. I just had never seen or thought to use them this way. Awesome! – chaotic3quilibrium Mar 22 '16 at 23:48
  • I used your solution in answering a different StackOverflow question related to finding cycles in a directed graph. I used it in the abstract case class towards the bottom of the answer: http://stackoverflow.com/a/36144158/501113 – chaotic3quilibrium Mar 23 '16 at 15:05
0

Case class parameters are vals by default, but you can set them to vars.

case class Node(identity: String, var childrenArg: List[Node], var customNodeArg: CustomNode)

Making them vars gives you getters and setters automatically

redeagle47
  • 1,355
  • 4
  • 14
  • 26
  • I don't think this doesn't solve my problem as both the `childrenArg` and the `customNodeArg` parameters are still properties; i.e. they are included in the compiler generated `equals` and `hashCode` function implementations. And while it was implied (and I have edited my question to now make it explicit), I need the case class to be immutable. And the presence of any `var` parameters makes the case class mutable. – chaotic3quilibrium Mar 22 '16 at 19:27