Consider two traits, TestTrait1 and TestTrait and assume NewObject extends both. The idea is to make use of variable in TestTrait1 in TestTrait. The below code works perfectly fine.
scala> trait TestTrait1 {
| val arguments1: Array[String] = Array("1","2")
| }
defined trait TestTrait1
scala> trait TestTrait {
| val arguments: Array[String]
| val len = arguments.length
| }
defined trait TestTrait
scala> object NewObject extends TestTrait1 with TestTrait {
| lazy val arguments = arguments1
| }
defined object NewObject
scala> NewObject
res30: NewObject.type = NewObject$@7c013560
Now replace TestTrait1 with App. Since arguments is set for lazy evaluation, I will assume that even in case of DelayedInit, the below code will work.
scala> object NewObject extends App with TestTrait {
| lazy val arguments = args
| }
But it doesn't. What is the reason behind this?
scala> NewObject
java.lang.NullPointerException
at TestTrait$class.$init$(<console>:12)
... 35 elided
If this is the case, what is the solution to use args in another trait similar to TestTrait here?
trait TestTrait1 {
val arguments1: Array[String] = Array("1","2")
}
trait TestTrait {
val arguments: Array[String]
val len = arguments.length
}
If you see the difference, the TestTrait has a member len
that would eagerly get initialized. But args
is a def
inside App
which happens to have a default value as null
. If you change the len
to lazy val
or def
it would not blow-up with NPE.
Let's try this on a quick REPL session:
scala> :paste
// Entering paste mode (ctrl-D to finish)
trait TestTrait {
def arguments: Array[String]
lazy val len = arguments.length
}
object NewObject extends App with TestTrait {
override lazy val arguments = super.args // Added `override` and `super` just for clarity.
}
// Exiting paste mode, now interpreting.
defined trait TestTrait
defined object NewObject
scala> NewObject
res0: NewObject.type = NewObject$@5ace1ed4
scala> NewObject.arguments
res1: Array[String] = null
If you want to reproduce the issue you can call len
as below:
scala> NewObject.len
java.lang.NullPointerException
at TestTrait$class.len(<console>:12)
at NewObject$.len$lzycompute(<console>:15)
at NewObject$.len(<console>:15)
... 33 elided
So, the answer to your question is you would need to make len
either lazy val
or def
if you want to invoke the instance of NewObject
. I would suggest making the NewObject a class
or trait
because you do not want an unsafe/eagerly initialized len
member that would blow-up with NPE.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With