考虑:
object implicitnull extends App {
mymethod
implicit val arg = "foo"
def mymethod(implicit arg: String) = {
arg.size
}
}
Run Code Online (Sandbox Code Playgroud)
但是,这不会导致任何编译错误,而是在运行时导致NullPointerException来自arg.size.
这是预期的行为吗?
是的,这是由于Scala构造类并初始化它们的方式所预期的行为.考虑这个例子:
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
Run Code Online (Sandbox Code Playgroud)
在f类中调用该函数时,C该值已初始化,而在类中B它尚未初始化.类A与类完全相同B- 唯一的区别是Scala arg隐式传递A.
这有点让人困惑,因为这段代码正在做两件事 - 它声明成员变量并执行构造函数代码.B例如,如果以类为例,val arg将在f被调用的位置声明,但尚未初始化.val arg = "foo"进行初始化.一旦你将它翻译成Java,它就会变得更加明显:
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";
}
}
Run Code Online (Sandbox Code Playgroud)
使用lazy val或更正初始化顺序将有助于解决它:
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
Run Code Online (Sandbox Code Playgroud)