Question

Consider:

object implicitnull extends App {
  mymethod

  implicit val arg = "foo"

  def mymethod(implicit arg: String) = {
    arg.size
  }
}

This does not cause any compilation error, however, at runtime results in NullPointerException coming from arg.size.

Is this the intended behavior?

Était-ce utile?

La solution

Yes it's the intended behavior due to the way Scala constructs classes and initializes them. Consider this example:

scala> class A {
     |   f
     | 
     |   implicit val arg = "foo"
     | 
     |   def f(implicit arg: String) = {
     |     println(arg)
     |   }
     | }
defined class A

scala> class B {
     |   f(arg)
     | 
     |   val arg = "foo"
     | 
     |   def f(implicit arg: String) = {
     |     println(arg)
     |   }
     | }
defined class B

scala> class C {
     |   implicit val arg = "foo"
     | 
     |   f
     | 
     |   def f(implicit arg: String) = {
     |     println(arg)
     |   }
     | }
defined class C

scala> new A
null
res0: A = A@67d3caf

scala> new B
null
res1: B = B@3f2c5ad4

scala> new C
foo
res2: C = C@177bdd23

At the moment the function f is invoked in class C the value has been initialized, while in class B it has been not initialized yet. Class A is exactly the same as class B - the only difference is that Scala passes the arg implicitly in A.

It's a little bit confusing because this code is doing 2 things - it's declaring member variables and executing constructor code. If you take class B for example, val arg would be declared at the point where f is called, but not initialized yet. val arg = "foo" does the initialization. Once you translate this to Java it becomes more obvious:

public class B {

    void f(String arg) {
        System.out.println(arg);
    }

    String arg; // also acts as final in Scala

    public B() {
        f(arg);
        arg = "foo";
    }
}

Using lazy val or correct initialization order will help to fix it:

scala> class B {
     |   f(arg)
     | 
     |   lazy val arg = "foo"
     | 
     |   def f(implicit arg: String) = {
     |     println(arg)
     |   }
     | }
defined class B

scala> new B
foo
res3: B = B@3f9ac6e6
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top