0

Usually the "memeber" in trait is defined as def variable:Type, then other memebers which depend on variable uses lazy val to prevent variable being null when getting initialized.

However if it is a piece of logic, e.g. a function call depends on variable will still throw null exception. Like the code below:

  trait A {

    def variable:Seq[String]

    if (variable.size > 3) // check
      println("too many strings")

  }


  case class B(vs:String*) extends A {
    override val variable: Seq[String] = vs
    //override def hi(): Unit = ???
  }

  val b = B("x", "y", "z")
  println(b)

This will throw error "A.variable() is null".

Strangely, if I wrote variable as given constructor parameter, the error is gone.

  case class B(override val variable:String*) extends A {
    //override def hi(): Unit = ???
  }

How I can delay the "check" and why the second case doesn't throw exception?

worldterminator
  • 2,968
  • 6
  • 33
  • 52
  • I would advise either using an `abstract class` or move such checks to another factory-like trait. – Luis Miguel Mejía Suárez Jan 26 '23 at 14:16
  • Nothing "strange": when your variable is defined as a constructor parameter, it is already defined by the time constructor is accessing it, as opposed to ... not. As to you question, just make it `lazy` in `B`. – Dima Jan 26 '23 at 14:25

1 Answers1

2

That's a use case for early initializers

case class B(vs: String*) extends {
  override val variable: Seq[String] = vs
} with A {
  //override def hi(): Unit = ???
}

In Scala, what is an "early initializer"?

https://docs.scala-lang.org/scala3/reference/dropped-features/early-initializers.html

Dmytro Mitin
  • 48,194
  • 3
  • 28
  • 66