Scala None实例not==None

Scala None实例not==None,scala,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 (ide

我在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}")
      ...
  }
问题是,当值实际为“无”时,有时会采用情况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
(第一行是我预期会发生的事情,实际上是正常发生的;其余是错误案例)

因此,如果我的日志记录是可信的(而且我没有弄乱我的表达式),那么我有一个例子,映射返回x,其中x是类scala.None$(与编译代码看到的类scala.None$相同)的实例,但与case 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$值不为null时引发异常,甚至将存储移到静态模块$value到静态构造函数块(原始代码在构造函数中有这个存储,我认为这在技术上违反了JVM规则,因为直到构造函数返回后才认为对象已初始化)

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

关于您的测试:
None
是Scala中的对象。定义
val none1=None
,您将此对象分配给
none1
,该对象应为单例对象

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

此外,如果您试图在对象测试中运行与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,而不是字符串

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

case x if(x.isInstanceOf[None$]) => 

请注意,在您的代码上下文中处理
None
的方式是通过
选项[a]
,在该选项中,不指定第二个条件的情况,意味着您允许
None
成为第二个情况的一部分

您应该做的是处理
映射。如果您试图获得的案例不是

val compStruct = subsqs.comp.get(ident)
compStruct match {
  case None => ...
  case x: Some(_) => ...
}

可能会显示subsq.comps的类型声明。另外,您是否正在从另一个虚拟机序列化任何内容,或者以某种方式(网络/磁盘)加载整个subsq…同时尝试显示已调试对象的哈希键,并进行如下比较:scala.None.hashCode,x.hashCode可能它们不匹配?None$不会重新定义equals()或hashCode(),因此它使用基本对象实现。因此,如果您能够以某种方式获得None$的第二个实例,它将不会==None。这与我的结果相匹配,但没有解释如何创建None$的另一个实例。None$提供readResolve()方法,返回静态模块$value,因此反序列化不应创建其他实例。实际上,有人使用反射调用私有构造函数方法可能会解释这种情况-这会将标准的None$更改为新的None$。但在这种情况下,Map.get会返回奇怪的None,这就是happe宁“立即”在Map.get从单独的Map.get返回预期的None之后,仍然令人费解。您的建议作为一种解决方法是有意义的,但在不了解问题的情况下,我不愿意尝试对其进行编码。您还可以尝试检查
System.identityHashCode
以确定这是否是另一个实例,这意味着添加类型anno将
HashMap[String,String]
转换为
a
compStruct
的操作应该让编译器指出问题所在,对吧?我想选择
java.io.Serializable
作为映射值的公共类型并不是程序员想要的。恰恰是@SvenKoschnicke!