Scala None 实例不是 == None

标签 scala

我在 Scala 代码中遇到了一个间歇性问题,我正在使用带有字符串键的不可变映射中的值。这是基本代码,包括我添加的调试日志:

  val compStruct = subsq.comps get (ident)
  compStruct match {
    ...
    case None =>
      logger.info(s"Found None, of type ${compStruct.getClass.getName}, at position $position (ident $ident)")
      ...
    case x =>
      logger.info(s"Illegal structure of type ${x.getClass.getName} at position $position (ident $ident) - x == None is ${x == None}, x.getClass == None.getClass is ${x.getClass == None.getClass}, x.getClass.getName == None.getClass.getName (${None.getClass.getName}) is ${x.getClass.getName == None.getClass.getName}")
      ...
  }

问题是当值实际上是 None 时,有时会采用 case x,如(已清理的)调试输出所示:
  INFO  ...: Found None, of type scala.None$, at position 3000 (ident XX)
  INFO  ...: Illegal structure of type scala.None$ at position 3200 (ident XX) - x == None is false, x.getClass == None.getClass is true, x.getClass.getName == None.getClass.getName (scala.None$) is true

(第一行是我期望发生的,并且确实正常发生;其余的是错误情况)

因此,如果要相信我的日志记录(并且我并没有以某种方式弄乱我的表达方式),我就会遇到 map 返回 x 的情况,其中 x 是 scala.None$ 类的实例(同一个 scala.None$ 类)。 None$ 如编译代码所见)但不匹配大小写 None 和 x == None 为假。

类加载问题将是明显的原因,但 x.class == None.class 似乎排除了这一点。

补充:正如我在评论中所建议的,我可以重现 None 与以下代码不匹配的实例:
object Test {
  def main(args: Array[String]): Unit = {
    val none1 = None
    val clas = this.getClass.getClassLoader.loadClass("scala.None$")
    val constr = clas.getDeclaredConstructors()(0)
    constr.setAccessible(true)
    val none2 = constr.newInstance()
    println(s"none1 == none2 is ${none1 == none2}")
    println(s"none1 == None is ${none1 == None}")
    println(s"none2 == None is ${none2 == None}")
  }
}

这使:
none1 == none2 is false
none1 == None is false
none2 == None is true

不过,我认为这与应用程序中发生的事情没有任何关系。

补充:我修改了实际的 None$ 类文件,以便在构造函数执行时打印一条消息,如果在调用构造函数时 None$.MODULE$ 值为非空,则抛出异常,甚至将存储移动到静态 MODULE$静态构造函数 block 的值(原始代码将此存储在构造函数中,我认为从技术上讲这违反了 JVM 规则,因为直到构造函数返回后才认为对象已初始化)。

这确实阻止了对复制问题症状的构造函数(上面的代码示例)的反射调用,但不会改变实际应用程序中的任何内容。 None$.MODULE$ 的值从代码的一次执行到下一次执行会发生变化,即使类保持不变(相同的 System.identityHashCode),但构造函数只被调用一次。

最佳答案

关于你的测试:None是 Scala 中的一个对象。当你定义val none1 = None ,您分配给 none1这个对象,它应该是一个单例。

通过使用反射,您绕过了私有(private)构造函数并创建了 None 类的新实例。 ==运算符只会返回 true如果两个指针指向同一个对象。
您可以使用 System.identityHashCode(none1) 验证这些对象的内存地址。并进行比较。

此外,如果您尝试在对象 Test 中对 none1 运行匹配,您将遇到匹配错误,因为 None 的第二个实例与 None 或 x 都不匹配。

我能够重现您的错误。通过运行此代码:

val a = Map("a" -> "b", "b" -> None)
a.get("b") match { 
   case None => print("None")
   case x => print("X") 
}  // Prints X
a.get("c") match { 
    case None => print("None")
    case x => print("X") 
} // Prints None

我知道这并不能解释为什么它会打印 X,但至少你知道什么时候......

因为您的 HashMap 没有任何值,所以它是 HashMap[String,java.io.Serializable] 而不是 HashMap[String,String]。
并且对 get 的调用将返回 java.io.Serializable 而不是 String。

要解决您的问题并使其在无时匹配,您可以执行以下操作:
case x if(x.isInstanceOf[None$]) => 

关于Scala None 实例不是 == None,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35641618/

相关文章:

scala - 为什么要解除触发方法?

scala - SBT 未解决 Squeryl 依赖关系

scala - 在 Scala 中创建元素差异为 1 的序列组?

scala - 在按名称参数 : evaluation is not forced? 中传递的函数上使用 apply ("()")

scala - 如何根据数据框中的另一列添加包含星期几的新列?

scala - 仅当要转换的函数具有至少两个参数时,才能将函数隐式转换为二阶函数

scala - 在 Scala 中进行柯里化(Currying) : multiple parameter lists vs returning a function

scala - 在 twitter chill 中处理案例类(Scala 接口(interface)到 Kryo)?

list - 在 Scala 中展平元组列表?

java - 使用 SBT 将 Artifact 发布到本地 Maven 仓库并在 ​​Gradle 项目中使用它