3

I am running an sbt application instantiates an object of class MyObject which uses a val from its companion object. Here is the main class(object):

object MainClass {

  def main(args: Array[String]) {
   val a = new MyObject()
  }
}

Here is the definition of MyObject:

import java.text.SimpleDateFormat
import java.util.Calendar


class MyObject {
  val aValue = MyObject.yesterday
}

object MyObject {

  val yesterday = getDaysAgo(1)

  val dateNumFormat = new SimpleDateFormat("yyyymmdd")

  private def getDaysAgo(n: Int) = {
    val today = Calendar.getInstance()
    today.add(Calendar.DAY_OF_MONTH, -n)
    //println(dateNumFormat.format(today.getTime))
    today.getTime
  }
}

When I uncomment println statement I get the (exact) following error:

$ sbt run
[info] Loading project definition from C:\Work\metaswitch\RandomProject\project
[info] Set current project to RandomProject (in build file:/C:/Work/metaswitch/RandomProject/)
[info] Compiling 1 Scala source to C:\Work\metaswitch\RandomProject\target\scala-2.11\classes...
[info] Running MainClass
[error] (run-main-0) java.lang.ExceptionInInitializerError
java.lang.ExceptionInInitializerError
    at MyObject.<init>(MyObject.scala:21)
    at MainClass$.main(MainClass.scala:6)
    at MainClass.main(MainClass.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
Caused by: java.lang.NullPointerException
    at MyObject$.getDaysAgo(MyObject.scala:13)
    at MyObject$.<init>(MyObject.scala:6)
    at MyObject$.<clinit>(MyObject.scala)
    at MyObject.<init>(MyObject.scala:21)
    at MainClass$.main(MainClass.scala:6)
    at MainClass.main(MainClass.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
[trace] Stack trace suppressed: run last compile:run for the full output.
java.lang.RuntimeException: Nonzero exit code: 1
    at scala.sys.package$.error(package.scala:27)
[trace] Stack trace suppressed: run last compile:run for the full output.
[error] (compile:run) Nonzero exit code: 1
[error] Total time: 2 s, completed 06-Jan-2016 11:21:16
eddyP23
  • 6,420
  • 7
  • 49
  • 87
  • 5
    What is `dateNumFormat`? How and where is that defined? Can you confirm that the line numbers in the stack trace are exactly corresponding to the lines of your example code (e.g. line 48 - what's that in your code) ? – 0__ Jan 05 '16 at 18:33
  • I have no idea why uncommenting the `println` would make this work, but after reading through http://stackoverflow.com/questions/12184997/scala-and-forward-references, I played around and found that if you switch the order of your definitions (object first then class) it works fine. – childofsoong Jan 05 '16 at 21:14
  • what does "running an sbt application" mean? – chad Jan 05 '16 at 22:15
  • 2
    Note that it's a "companion object", not a "component object". – Seth Tisue Jan 06 '16 at 00:26
  • @soong Tried swapping them round, it didn't help. – eddyP23 Jan 06 '16 at 11:26
  • @SethTisue Thanks for correcting me. – eddyP23 Jan 06 '16 at 11:26
  • @0__ Sorry, forgot to add `dateNumFormat`, though I don't think that is the problem here. :( – eddyP23 Jan 06 '16 at 11:26
  • 4
    Initialisation of fields happens in textual order ([JLS](https://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4), but also true for Scala AFAIK). Given the order of declarations in your code, having `yesterday` be declared first, it will be initialized first calling `getDaysAgo`, which in turn will read the uninitialized `dateNumFormat` field, which (at this point) will still have its default value of `null`. – Dirk Jan 06 '16 at 11:38
  • @Dirk that is correct, you should post that as an answer – 0__ Jan 06 '16 at 11:50

1 Answers1

4

The initialisation of fields happens in textual order (i.e., in the order, the fields are declared in the class/object).

Given the order of declarations in your code, having yesterday be declared first, it will be initialized first resulting in a call to getDaysAgo. That method, in turn, tries to read the (as of yet uninitialized) field dateNumFormat, which yields null, resulting in the expression dateNumFormat.format(today.getTime) to throw a NullPointerException.

The easiest fix is, to move the declaration (and initialization) of dateNumFormat before that of yesterday -- as you have already discovered.

Dirk
  • 30,623
  • 8
  • 82
  • 102