我在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}")
...
}
问题在于,当值实际为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
(第一行是我期望发生的,确实确实发生;其余的是错误情况)
所以,如果我的日志记录被认为(并且我没有以某种方式弄乱我的表达)我有一个地图返回x的情况,其中x是类scala.None $的实例(< em>相同的类scala.None $,如编译代码所示)但与案例None不匹配且x == None为false。
类加载问题是明显的原因,但x.class == None.class似乎排除了这一点。
补充:正如我在评论中建议的那样,我可以重现与以下代码不匹配的无实例:
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 $ classfile,以便在构造函数执行时打印消息,如果在调用构造函数时None $ .MODULE $值为非null,则抛出异常,甚至将存储移动到静态构造函数块的静态MODULE $值(原始代码在构造函数中有这个存储,我认为这在技术上违反了JVM规则,因为在构造函数返回之前不会将对象视为初始化)。
这会阻止对构造函数(上面的代码示例)的反射调用,该构造函数会复制问题的症状,但不会更改实际应用程序中的任何内容。 None $ .MODULE $的值从一次执行代码变为下一次,即使该类保持不变(相同的System.identityHashCode),但构造函数只被调用一次。
答案 0 :(得分: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
我知道这并没有解释为什么它打印X,但至少你知道什么时候......
因为你的HashMap没有值,所以它是HashMap [String,java.io.Serializable]而不是HashMap [String,String]。 对get的调用将返回java.io.Serializable而不是String。
要解决您的问题并在无问题时使其匹配,您可以执行以下操作:
case x if(x.isInstanceOf[None$]) =>
答案 1 :(得分:0)
请注意,在代码上下文中处理None
的方式是通过Option[A]
,而不指定第二个条件的情况,这意味着您允许None
成为第二种情况的一部分。
如果您想要获得的不是map.get
None
val compStruct = subsqs.comp.get(ident)
compStruct match {
case None => ...
case x: Some(_) => ...
}