我有一个抽象类,其中有一个未实现的方法numbers
,该方法返回一个数字列表,该方法用于另一个val属性初始化:
abstract class Foo {
val calcNumbers = numbers.map(calc)
def numbers: List[Double]
}
实现类使用val表达式实现:
class MainFoo extends Foo {
val numbers = List(1,2,3)
}
这可以很好地编译,但是在运行时会引发NullPointerException并指向val calcNumbers
行:
[error] (run-main-0) java.lang.ExceptionInInitializerError
[error] java.lang.ExceptionInInitializerError
...
[error] Caused by: java.lang.NullPointerException
...
但是,当我将实现的方法更改为def时,它会起作用:
def numbers = List(1,2,3)
那是为什么?它与初始化顺序有关吗?由于没有编译时错误/警告,将来如何避免这种情况? Scala如何允许这种不安全的操作?
答案 0 :(得分:3)
这是代码初始化MainFoo
时试图执行的操作:
val calcNumbers
和val numbers
,最初设置为0
。Foo
的初始化程序,在初始化numbers.map
的同时尝试调用calcNumbers
。MainFoo
的初始化程序,在此它将numbers
初始化为List(1, 2, 3)
。由于numbers
尚未初始化,当您尝试在val calcNumbers = ...
中访问它时,会得到NullPointerException
。
可能的解决方法:
numbers
中将MainFoo
设为def
numbers
中将MainFoo
设为lazy val
calcNumbers
中将Foo
设为def
calcNumbers
中将Foo
设为lazy val
每种解决方法都可以防止对未初始化的值numbers.map
进行热切的值初始化调用numbers
。
FAQ提供了其他一些解决方案,并且还提到了(昂贵的)编译器标志-Xcheckinit
。
您可能还会发现这些相关的答案很有用: