我在Scala代码中遇到了一个间歇性的问题,我的代码与具有字符串键的不可变map中的值一起工作。以下是基本代码,包括我添加的调试日志:
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,如(经过消毒的)调试输出所示:
问题在于当值实际上为 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
第一行是我预期发生的,通常也确实会发生;其他情况则是错误的。
因此,如果我的日志记录是可信的(并且我没有以某种方式搞砸我的表达式),则我有一个情况,其中地图返回x,其中x是scala.None$类的一个实例(由编译后的代码看到的相同的scala.None$类),但不匹配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}")
}
}
这意味着:
none1 == none2 is false
none1 == None is false
none2 == None is true
我不认为这与应用程序中正在发生的事情有任何关系。
补充:我修改了实际的None$类文件,使其在构造函数执行时打印消息,并且如果在调用构造函数时None$.MODULE$值非空,则会抛出异常,甚至将存储器移动到静态构造函数块中的静态MODULE$值(原始代码在构造函数中具有此存储器,但我认为这在技术上违反了JVM规则,因为对象在构造函数返回之后才被认为已初始化)。
这确实阻止了对构造函数的反射调用(以上示例代码),该调用复制了问题的症状,但并未改变实际应用程序中的任何内容。 None$.MODULE$的值从一次代码执行到下一次执行会发生变化,尽管类保持不变(System.identityHashCode相同),但构造函数仅调用一次。
System.identityHashCode
来判断是否为另一个实例。 - Odomontois