为什么对对象的 val 的 scala 测试断言会抛出 NullPointerException? [英] Why a scala test assertion on an object's val throws NullPointerException?

查看:63
本文介绍了为什么对对象的 val 的 scala 测试断言会抛出 NullPointerException?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在测试 Scala 代码时,我在对来自对象的值进行断言时遇到了一个奇怪的 NPE.

这是重现问题的最少代码:

ma​​in/scala/Playground.scala:

object Playground extends App {val 问候 = 你好 Scala"打印(问候)}

test/scala/PlaygroundSpec.scala:

import org.scalatest.wordspec._类 PlaygroundSpec 扩展了 AnyWordSpec {游乐场代码"应该 {打个招呼"在 {assert(Playground.greeting.contains("Hello"))//抛出 NPE,因为 greeting 为空.如何???}}}

示例程序运行良好并打印hello Scala",但测试在断言行抛出 NullPointerException,因为 greetingnull.

如果用字符串常量初始化greeting怎么可能是null?

<块引用>

注意:val 声明中添加 lazy 使其工作并且测试通过.

解决方案

In Scala 2 App extends DelayedInit,所以编译器神奇地重写了初始化代码,这样字段的初始化就移动到了 delayedInit 方法例如,

object Playground 扩展了 App {val 问候 = 你好 Scala"打印(问候)}

变得像

object Playground 扩展了 App {私有变量问候语:String = null定义问候():字符串 = 问候def delayInit(): Unit = {问候 = 你好 Scala"打印(问候())}def main(args: Array[String]) = {//间接调用delayInit...}}

现在我们可以看到

assert(Playground.greeting.contains(Hello"))

变成

assert(null.contains(Hello"))

as delayedInit 方法没有被调用.为了证明这一点,请观察以下内容的工作原理

Playground.main(Array.empty)//delayInit 被间接调用assert(Playground.greeting.contains("Hello"))//好的

<块引用>

在 val 声明中添加惰性使其工作并且测试通过.

这是可行的,因为lazy val greeting 有效地将字段转换为一种方法,将其移出初始化代码,因此它不会成为delayedInit 的一部分.

显然这令人困惑,所以 Scala 3 丢弃:Delayedinit.

While testing Scala code, I've run into a strange NPE while asserting on a value from an object.

Here is the minimal code to reproduce the issue:

main/scala/Playground.scala:

object Playground extends App {
  val greeting = "Hello Scala"
  println(greeting)
}

test/scala/PlaygroundSpec.scala:

import org.scalatest.wordspec._

class PlaygroundSpec extends AnyWordSpec {
  "The playground code" should {
    "say hello" in {
      assert(Playground.greeting.contains("Hello")) // Throws NPE because greeting is null. How???
    }
  }
}

The sample program run just fine and prints "hello Scala", but the test throws a NullPointerException on the assertion line, because greeting is null.

How could greeting be null if it is initialized with a string constant?

Note: Adding lazy to the val declaration makes it work and the test passes.

解决方案

In Scala 2 App extends DelayedInit, so compiler magically rewrites initialisation code such that the initialisation of fields is moved to delayedInit method, for example,

object Playground extends App {
  val greeting = "Hello Scala"
  println(greeting)
}

becomes something like

object Playground extends App {
  private var greeting: String = null
  def greeting(): String = greeting

  def delayedInit(): Unit = {   
    greeting = "Hello Scala"
    println(greeting()) 
  }

  def main(args: Array[String]) = {
    // indirectly call delayedInit
    ...
  }
}

Now we can see

assert(Playground.greeting.contains("Hello"))

becomes

assert(null.contains("Hello"))

as delayedInit method did not get called. To prove the point observe how the following works

Playground.main(Array.empty) // delayedInit gets indirectly called
assert(Playground.greeting.contains("Hello")) // ok

Adding lazy to the val declaration makes it work and the test passes.

This works because lazy val greeting effectively turns the field into a method which moves it out of the initialisation code so it does not become part of delayedInit.

Clearly this is confusing so Scala 3 Dropped: Delayedinit.

这篇关于为什么对对象的 val 的 scala 测试断言会抛出 NullPointerException?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆