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
}
Luigi Plinge
  • 50,650
  • 20
  • 113
  • 180

2 Answers2

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")
Eric
  • 15,494
  • 38
  • 61
  • 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. – Luigi Plinge Sep 22 '11 at 00:56
  • 10
    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. – Kipton Barros Sep 22 '11 at 02:54
  • 2
    @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](http://stackoverflow.com/questions/7510060/explain-this-type-mismatch-in-lazy-evaluation) for more. – Michael Lorton Sep 22 '11 at 05:49
  • @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. – Luigi Plinge Sep 22 '11 at 07:57
  • 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). – Martin Konicek Oct 29 '12 at 17:48
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.

Community
  • 1
  • 1
JaimeJorge
  • 1,885
  • 16
  • 15