2012-05-25 10 views
30

Come prova, ho scritto questo codice:Se un Int non può essere nullo, cosa significa null.asInstanceOf [Int]?

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 che lo scorso invocazione di f(), dicendo che era ambiguo. Il compilatore accettò, e prodotto questo output:

Int 
String 
String 

Ora sto cercando di indovinare che questo ha a che fare con il fatto che non è un Int AnyRef, quindi l'unica versione di F che lavora per f (null) è f (x: stringa). 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 funziona. Dopo tutto, se provo a lanciare una stringa in 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) 
     ... 

Certo che c'è da aspettarselo - "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?

+0

Oh, e come potrei averlo ulteriormente capito da solo, usando il REPL o il scalap o qualche indicatore del compilatore magico o ...? – AmigoNico

+0

Un'altra [domanda] simile (http://stackoverflow.com/q/8285916/617996) ... – PrimosK

+0

Dai un'occhiata a un recente aggiornamento delle specifiche https://github.com/scala/scala-dist/pull/20 e pubblichi https://issues.scala-lang.org/browse/SI-4437, che spiega parte della storia di fondo. È un po 'paragonabile a 'default (T)' in C#, afaik. – soc

risposta

46

Il comportamento di casting null in un Int dipende dal contesto in cui è stato eseguito.

Prima di tutto, se lanci una null a un Int, in realtà significa 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), quindi questo valore non viene ulteriormente convertito, ecco perché println(null.asInstanceOf[Int]) stampa null.

Tuttavia, se si utilizza questo stesso valore intero in scatola in un contesto in cui si prevede un primitivo Int (Java int), esso verrà convertito in un primitivo, e null è (come valore di default per i tipi di riferimento) venga convertito in 0 (un valore predefinito per i tipi primitivi).

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

Tuttavia, se questo metodo è specializzato, il suo tipo restituito è Int (che è un numero intero primitivo in questo caso), pertanto il valore null: Any deve essere convertito in 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 
+0

Dettaglio molto utile - grazie! – AmigoNico

+0

Non mi sono reso conto che Int è un tipo primitivo – zinking

4

Sembra come se fosse una semplice conversione automaticamente a zero:

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

E, naturalmente, 0, a differenza di nulla, può essere un Int.

+0

Molto interessante. Sono imbarazzato che mentre ho chiesto al repl per il * tipo * di null.asInstanceOf [Int], non mi è mai venuto in mente di chiedere il suo * valore *, che assurdamente credevo fosse nulla. Grazie! Questa conversione mi sorprende un po ', però - hmmm. – AmigoNico

+1

Sorprende anche me. Non ho idea se sia il comportamento previsto, ma mi sembra sbagliato. – dhg

+1

Questo è il comportamento previsto - si ottiene lo stesso valore da 'var x: X = _' come si fa con' var x = null.asInstanceOf [X] 'per qualsiasi' X'. –

20

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

Ora, se si desidera sapere perché restituisce 0, è perché asInstanceOf funziona su AnyRef, non su AnyVal. Quando viene applicato a un AnyVal, userà la versione in scatola, invece, e una scatola null ha valore 0.

+0

Molto chiaro e conciso - grazie! – AmigoNico

0

In primo luogo, 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

In secondo luogo, perché quando facciamo println(null.asInstanceOf[Int]), dà null?
Ciò è dovuto all'implementazione di println. E alla fine chiama il metodo java String.valueOf, che è

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

Se fate un null.asInstanceOf[Int] == null nel guscio, verrà restituito vero, ma dà un avviso contrario che "confrontando i valori di tipo Int e Null usando` ==' darà sempre false ". Penso che questo potrebbe essere un problema nella cancellazione del tipo di scala.

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

Tra l'altro, si può ancora fare un f (null) senza alcun errore di compilazione perché Scala sta facendo una conversione implicita per voi

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

Tuttavia, lo vedrete fa saltare in aria in fase di esecuzione.