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}")
      ...
  }

проблема заключается в том, что случай x иногда принимается, когда значение фактически является None, как показано отлаживаемым результатом отладки:

  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 is 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 $.MODULE $не равно null при вызове конструктора и даже перемещает хранилище в статическое значение MODULE $для блока статического конструктора (исходный код имел это хранилище в конструкторе, что, по моему мнению, является технически нарушением правил JVM, поскольку объект не считается инициализированным до тех пор, пока не вернется конструктор).

Это блокирует вызов отражения конструктору (выше образца кода), который дублирует симптомы проблемы, но ничего не меняет в фактическом приложении. Значение None $.MODULE $изменяется от одного выполнения кода к другому, даже если класс остается тем же (тот же System.identityHashCode), но конструктор вызывается только один раз.

Ответ 1

Что касается вашего теста: None - это объект в Scala. Когда вы определяете val none1 = None, вы назначаете none1 этому объекту, который должен быть одиночным.

Используя отражение, вы обходите частный конструктор и создаете новый экземпляр класса None. Оператор == возвращает только true, если два указателя указывают на один и тот же объект. Вы можете проверить адрес памяти этих объектов, используя System.identityHashCode(none1) и сравнить его.

Кроме того, если вы попытаетесь выполнить совпадение с none1 в объекте Test, вы столкнетесь с ошибкой совпадения, так как второй экземпляр 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 имеет значения None, это HashMap [String, java.io.Serializable], а не HashMap [String, String]. И вызов get вернет java.io.Serializable, а не String.

Чтобы решить вашу проблему и заставить ее соответствовать, когда она отсутствует, вы можете сделать:

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

Ответ 2

Обратите внимание, что способ None обрабатывается в вашем контексте кода через Option[A], где, не указывая случай для вашего второго условия, это означает, что вы разрешаете None быть частью второй случай.

Что вы должны сделать, так это обработать map.get таким образом, если вы пытаетесь добиться получения кода, который не является None

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