Se un Int non può essere nullo, cosa significa null.asInstanceOf ?

Come test, ho scritto questo codice:

object Ambig extends App { def f( x:Int ) { println("Int" ) } def f( x:String ) { println("String") } f( null.asInstanceOf[Int ] ) f( null.asInstanceOf[String] ) f(null) } 

Mi aspettavo di ottenere un errore sull’ultima invocazione di f (), dicendo che era ambiguo. Il compilatore l’ha accettato e ha prodotto questo risultato:

 Int String String 

Ora suppongo che questo abbia a che fare con il fatto che Int non è un AnyRef, quindi l’unica versione di f che funziona per f (null) è f (x: String). Ma allora, se un Int non può essere nullo, cosa vuol dire null.asInstanceOf [Int]? Il repl dice che è di tipo Int:

 scala> :type null.asInstanceOf[Int] Int 

ma non vedo davvero come funzioni. Dopotutto, se provo a lanciare una stringa su un Int, si scatena l’inferno:

 scala> "foo".asInstanceOf[Int] java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source) ... 

Naturalmente c’è da aspettarsi – “foo” non può essere trasformato in un Int. Ma nessuno dei due può null, quindi perché il casting null su un Int funziona? Presumibilmente il pugilato in qualche forma, ma il tipo è ancora Int, che non può essere nullo …

Cosa mi manca?

Il comportamento di trasmettere null a un Int dipende dal contesto in cui viene eseguito.

Prima di tutto, se lanci un null in un Int , significa in realtà un intero in scatola, il cui valore è null . Se si inserisce l’espressione in un contesto in cui il tipo previsto è Any (che viene tradotto in Object dietro la scena, perché nel bytecode JVM, non c’è modo di fare riferimento a un tipo primitivo e un tipo di riferimento con lo stesso riferimento), allora questo valore non viene ulteriormente convertito – ecco perché println(null.asInstanceOf[Int]) stampa null .

Tuttavia, se si utilizza lo stesso valore intero in box in un contesto in cui è previsto un Int primitivo (Java int ), verrà convertito in primitivo e null è (come valore predefinito per i tipi di riferimento) convertito in 0 (un valore predefinito valore per i tipi primitivi).

Se un cast generico fa questo cast, quindi, naturalmente, si ottiene un null back.

Tuttavia, se questo metodo è specializzato, il suo tipo restituito è Int (che è un numero intero primitivo in questo caso), quindi null: Any valore deve essere convertito in un primitivo, come prima.

Quindi, in esecuzione:

 object Test extends App { println(null.asInstanceOf[Int]) def printit(x: Int) = println(x) printit(null.asInstanceOf[Int]) def nullint[T] = null.asInstanceOf[T] println(nullint[Int]) def nullspecint[@specialized(Int) T] = null.asInstanceOf[T] println(nullspecint[Int]) } 

produce:

 null 0 null 0 

Ecco la cosa: asInstanceOf non ha senso. Ciò che questo metodo fa è dire al compilatore di FERMARE IL SENSO, e fidati di quello che stai dicendo.

Ora, se vuoi sapere perché restituisce 0, è perché asInstanceOf funziona su AnyRef , non su AnyVal . Se applicato a un AnyVal , utilizzerà invece la versione in box e un valore null box ha valore 0.

Sembra che lo stia convertendo automaticamente a zero:

 scala> null.asInstanceOf[Int] res0: Int = 0 

E, ovviamente, 0, diversamente da null, può essere un Int .

Innanzitutto, siamo tutti d’accordo che non possiamo assegnare null a scala.Int come documentato in http://www.scala-lang.org/api/current/index.html#scala.Null

Secondo, perché quando println(null.asInstanceOf[Int]) , restituisce null ?
Questo è dovuto all’implementazione di println. Alla fine chiama il metodo java String.valueOf , che è

 return (obj == null) ? "null" : obj.toString(); 

Se si esegue un null.asInstanceOf[Int] == null nella shell, verrà restituito true, ma viene fornito un avviso opposto che “confrontando i valori dei tipi Int e Null utilizzando` == ‘verrà sempre restituito false “. Penso che questo potrebbe essere un problema nella cancellazione del tipo di scala.

Fare un println richiede solo una scala.Qualsiasi tipo, quindi il casting di null.asInstanceOf[Int] realtà non si è ancora verificato. Quindi, dobbiamo solo ricordare che quando assegni null.asInstanceOf[Int] a un Int, il cast avviene a runtime sulla base della semantica di cancellazione di Scala, e assegna a esso 0.

A proposito, puoi ancora fare af (null) senza alcun errore di compilazione perché scala sta facendo una conversione implicita per te

  null -> java.lang.Integer -> scala.Int 

Tuttavia, lo vedrai esplodere in fase di esecuzione.