0

I have been bitten a number of times by a situation like this:

trait MyTrait {
  val implementMe: String

  val upper = implementMe.toUpperCase
}

class MyClass(s: String) extends MyTrait {
  override val implementMe: String = s
}

val c = new MyClass("Hello, World")

println(c.upper)

In this example, everything compiles but the last line throws a NullPointerException at runtime, (I assume) because upper is computed before implementMe is actually defined.

I know I can fix it by changing upper to a def or a lazy val but it seems like this should be caught at compile time rather than runtime. Am I doing something wrong/is there a way this sort of problem can be detected at compile time? Is there a reason the compiler allows this?

  • Does [this](https://stackoverflow.com/questions/51426017/why-does-implement-abstract-method-using-val-and-call-from-superclass-in-val-exp#51426770) or [this](https://stackoverflow.com/questions/51426017/why-does-implement-abstract-method-using-val-and-call-from-superclass-in-val-exp#51426770) answer your question? The first answer does mention the `-Xcheckinit` flag. As you can see, the question reoccurs often enough that I've started to build my own hyperlinked network of nearly-duplicate answers on this topic... – Andrey Tyukin Jul 29 '18 at 12:52
  • Here is another very similar question: [Scala initialization order of vals](https://stackoverflow.com/questions/14568049/scala-initialization-order-of-vals). – Andrey Tyukin Jul 29 '18 at 12:55
  • Thanks very much, I'm glad I'm not the only one with this issue. I could not get the compiler flag to throw any error or warning on this but I'll keep playing around. Ultimately it's not a blocking issue, just annoying – Jonathan Cole Jul 30 '18 at 13:11
  • The flag still does not throw any compile-time errors, but it will crash with a more-or-less precise error message the first time you access uninitialized fields, intstead of letting the `null`s and `0`s escape into some remote corners of your code. The description says it's rather for testing. If one of the answers solves your problem, I'd vote to close as duplicate. – Andrey Tyukin Jul 30 '18 at 13:21

1 Answers1

0

The problem is initialization, as you've stated. As far as I know, there is no way to detect this at compile time. There is a bit of a trick, called early initialization, that allows you to work around this while avoiding def or lazy val (if that is your goal):

class MyClass(s: String) extends { override val implementMe: String = s } with MyTrait
Lasf
  • 2,536
  • 1
  • 16
  • 35