Scala无实例不= =无

Den*_*ski 10 scala

我在Scala代码中遇到了一个间歇性的问题,我正在处理来自带有String键的不可变映射的值.这是基本代码,包括我添加的调试日志:

  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}")
      ...
  }
Run Code Online (Sandbox Code Playgroud)

问题是,当值实际为None时,有时会采用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
Run Code Online (Sandbox Code Playgroud)

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

因此,如果我的日志记录被相信(并且我没有以某种方式弄乱我的表达)我有一个地图返回x的情况,其中x是类scala.None $的实例(相同的类scala.编译代码看不到$,但与案例None不匹配,x == None为false.

类加载问题是明显的原因,但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}")
  }
}
Run Code Online (Sandbox Code Playgroud)

这使:

none1 == none2 is false
none1 == None is false
none2 == None is true
Run Code Online (Sandbox Code Playgroud)

不过,我认为这与应用程序中发生的事情无关.

补充:我修改了实际的None $ classfile,以便在构造函数执行时打印消息,如果在调用构造函数时None $ .MODULE $值为非null,则抛出异常,甚至将存储移动到静态MODULE $静态构造函数块的值(原始代码在构造函数中有这个存储,我认为这在技术上违反了JVM规则,因为在构造函数返回之前不会将对象视为初始化).

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

Vin*_*ana 1

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

通过使用反射,您可以绕过私有构造函数并创建 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
Run Code Online (Sandbox Code Playgroud)

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

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

要解决您的问题并使其在 None 时匹配,您可以执行以下操作:

case x if(x.isInstanceOf[None$]) => 
Run Code Online (Sandbox Code Playgroud)