ragazzojp
ragazzojp

Reputation: 519

Pass to a case class constructor the instance being constructed

I have a complex set of traits and case classes, but for the sake of the discussion let me summarize what I'm trying to model in this way:

case class X(x: X)

That's fine until I end up initializing one of the instances this way:

val myX: X = X(myX)

It compiles (unbelievably), but the myX passed to the constructor is actually null, easily visible by checking myX.x afterwards.

Suppose having an instance pointing to itself is fine for what I'm trying to model, is there an elegant way to solve this issue and have the new instance of X with a pointer to itself? I mean without mutating the object after its creation, nor introducing a 2nd constructor that takes no parameter and uses this inside. Remember please the real situation is more complex that what sketched here.

Upvotes: 0

Views: 96

Answers (2)

Jasper-M
Jasper-M

Reputation: 15086

If you need to have a case class, I don't think it can be done. Case classes cannot have lazy parameters, and if they could point to themselves most of their methods would probably blow the stack. With a regular class you can do this:

scala> class X(x0: =>X) {
     |   lazy val x = x0 
     | }
class X

scala> val x: X = new X(x)
val x: X = X@5f98cb6f

scala> x.x
val res0: X = X@5f98cb6f

scala> x.x.x
val res1: X = X@5f98cb6f

If you want case class-like behavior you'll have to implement the boilerplate (apply, unapply, copy, equals, hashcode, ...) yourself, and be very careful not to trigger an infinite loop or a stack overflow.

Upvotes: 6

Mario Galic
Mario Galic

Reputation: 48400

Ill-advisedly, consider "simulating" by-name parameters in case classes like so

case class X(v: () => X) {
  def x: X = v.apply
}

val myX: X = X(() => myX)

however note the warning by Jasper-M.

Upvotes: 2

Related Questions