Scala 为什么在val表达式中使用val实现抽象方法并从超类调用返回NullPointerException
我有一个抽象类,它有一个未实现的方法Scala 为什么在val表达式中使用val实现抽象方法并从超类调用返回NullPointerException,scala,object-initialization,Scala,Object Initialization,我有一个抽象类,它有一个未实现的方法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) } 这可以很好地编译,但在运行时它抛出一个NullPointer
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是如何允许这种不安全的操作的?下面是您的代码在初始化
MainFoo
时尝试执行的操作:
val calcNumbers
和val numbers
分配一块内存,初始设置为0
Foo
的初始值设定项,在初始化calcNumbers
时尝试调用numbers.map
MainFoo
的初始值设定项,将numbers
初始化为列表(1、2、3)
val calcNumbers=…
中尝试访问时,numbers
尚未初始化,因此会出现NullPointerException
可能的解决办法:
MainFoo
adef
MainFoo
alazy val
Foo
adef
Foo
alazy val
numbers.map
提供了一些其他的解决方案,它还提到了(昂贵的)编译器标志-Xcheckinit
您可能还会发现以下相关答案很有用:
number
中缺少s
?可能是@HüseyinZengin的重复项,似乎有点太模糊了,不是吗?另一个非重复:(在这里,OP只是交换了val
s的顺序,没有抽象类,没有def by val覆盖)。一个更好的复制品:,但是答案可以更详细。@Andreytukin你是对的,这个问题本身不是一个完全的复制品,但这个问题基本上是早期初始化的结果。答案完全覆盖了这个问题,谢谢你的解释。虽然当Scala允许这个漏洞/陷阱而没有默认警告时,我感到震惊…@texasbruce哦,对象初始化很难。它在具有继承、特征、异常、垃圾收集、类加载器、内置单例和多线程的语言中都能工作,这是一个奇迹……为了避免这个问题,应该禁止在Scala中使用val
重写方法。仅允许def
和lazy val