考虑:
object implicitnull extends App {
mymethod
implicit val arg = "foo"
def mymethod(implicit arg: String) = {
arg.size
}
}
这不会导致任何编译错误,但是,在运行时导致NullPointerException
来自arg.size
。
这是预期的行为吗?
答案 0 :(得分:9)
是的,由于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
目前在类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";
}
}
使用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