Ho un problema intermittente nel codice Scala in cui sto lavorando con i valori di una mappa immutabile con chiavi stringa. Ecco il codice di base, tra cui la registrazione di debug ho aggiunto:Scala Nessuna istanza non == Nessuna
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}")
...
}
il problema è che il caso X è talvolta presa quando il valore è in realtà un None, come dimostra la (sterilizzata) output di debug:
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
(La prima linea è quello che mi aspetto di accadere e in effetti avviene normalmente, il resto è il caso di errore)
Quindi se il mio registro è da credere (e non ho incasinato la mia espressione in qualche modo) Ho un caso in cui la mappa restituisce x, dove x è un'istanza di classe scala.None $ (lo stesso classe scala.None $ come visto dal codice compilato) ma non corrisponde al caso Nessuno e x == Nessuno è falso.
Un problema di caricamento in classe sarebbe la causa ovvia, ma x.class == None.class sembra precluderlo.
Aggiunto: Come ho suggerito nei commenti, posso riprodurre le istanze di Nessuno che non corrispondono con il seguente codice:
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}")
}
}
che dà:
none1 == none2 is false
none1 == None is false
none2 == None is true
non credo che ha qualcosa a che fare con ciò che sta accadendo nell'applicazione, però.
Aggiunto: Ho modificato il file di classe None $ effettivo per stampare un messaggio quando il costruttore esegue e genera un'eccezione se il valore $ None $ .MODULE non è nullo quando viene chiamato il costruttore, e anche spostato lo store in il valore statico di MODULE $ al blocco di costruzione statico (il codice originale aveva questo store nel costruttore, che a mio avviso è tecnicamente una violazione delle regole JVM poiché l'oggetto non è considerato inizializzato fino a quando non viene restituito il costruttore).
Ciò blocca la chiamata di riflessione al costruttore (sopra il codice di esempio) che ha duplicato i sintomi del problema, ma non modifica nulla nell'applicazione effettiva. Il valore di None $ .MODULE $ cambia da un'esecuzione del codice a quella successiva, anche se la classe rimane la stessa (stesso System.identityHashCode), tuttavia il costruttore viene chiamato solo una volta.
Forse mostrare la dichiarazione del tipo di subsq.comps. Inoltre stai serializzando qualcosa in gioco qui da un'altra macchina virtuale o caricando l'intero subsq in qualche modo (rete/disco?). Prova anche a mostrare la chiave hash dei tuoi oggetti debug e confronta in questo modo: scala.None.hashCode, x .hashCode forse non corrispondono? – LaloInDublin
I tipi non sono davvero rilevanti qui in alcun modo che possa vedere, solo il None. None $ è una classe singleton in Scala, con un costruttore privato chiamato solo da un blocco di inizializzazione statico, Il costruttore che imposta il valore di.MODULE $ finale statico sull'istanza della classe (non sono sicuro del motivo per cui questo viene eseguito nel costruttore - approccio interessante). –
Nessuno $ non ridefinisce equals() o hashCode(), quindi sta utilizzando le implementazioni dell'oggetto di base. Quindi se puoi in qualche modo ottenere una seconda istanza di None $ non dovrebbe essere == Nessuna. Ciò corrisponde ai miei risultati, ma non spiega come potrebbe essere creata un'altra istanza di None $. Nessuno $ fornisce il metodo readResolve(), restituendo il valore statico $ MODULE, quindi la deserializzazione non dovrebbe creare un'istanza diversa. –