Instantiating immutable paired objects
Asked Answered
P

2

29

Is it possible to create a class with an immutable reference to a partner object, or does it have to be a var that I assign after creation?

e.g.

class PairedObject (p: PairedObject, id: String) {
  val partner: PairedObject = p  // but I need ref to this object to create p!
}

or similarly how could I instantiate the following pair?

class Chicken (e: Egg) { 
  val offspring = e
}

class Egg (c: Chicken) {
  val mother = c
}
Potherb answered 21/9, 2011 at 23:40 Comment(0)
W
28

Here is a complete solution to the Chicken/Egg problem:

class Chicken (e: =>Egg) { 
  lazy val offspring = e 
}

class Egg (c: =>Chicken) {
  lazy val mother = c
}

lazy val chicken: Chicken = new Chicken(egg)
lazy val egg: Egg         = new Egg(chicken)

Note that you have to provide explicit types to the chicken and egg variables.

And for PairedObject:

class PairedObject (p: => PairedObject, val id: String) {
  lazy val partner: PairedObject = p
}

lazy val p1: PairedObject = new PairedObject(p2, "P1")
lazy val p2: PairedObject = new PairedObject(p1, "P2")
Waterline answered 22/9, 2011 at 0:16 Comment(5)
I added solution for PairedObject as well. Also I found that vals for chicken and egg don't need to be lazy. Strictly you don't need to include types for both, as one can be inferred.Potherb
This solution will work if entered at the top level of a class or object definition, but one gets an illegal forward reference error if chicken and egg are local variables (say, inside a function). A solution that works in any scope is: lazy val (egg: Egg, chicken: Chicken) = ..., which can be self-referential.Khan
@KiptonBarros -- I think you should write up your comment as an answer, because, sorry Eric, as ingenious as the response was, it was woefully incomplete without KB's improvement. See here for more.Petiolate
@Malvolio that was my fault: I tested this in 2.9 in an App object which explains why lazy was not required. More generally it will be within a function where it is, as @Kipton points out. I've editied it back to Eric's original.Potherb
This is a good solution, but note that the lazy val is a little bit of "cheating", because the compiler generates a variable that can be internally mutated once (on first access).Rhiamon
T
4

If your problem is circular references, you could use the solution posted in this SO question:

scala: circular reference while creating object?

This solves the chicken/egg problem.

Teheran answered 22/9, 2011 at 0:7 Comment(1)
Works well if I just have a couple of case objects: just make the val lazy.Potherb

© 2022 - 2024 — McMap. All rights reserved.