Si un Int no puede ser nulo, ¿qué significa null.asInstanceOf ?

Como prueba, escribí este código:

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

Esperaba obtener un error en esa última invocación de f (), diciendo que era ambiguo. El comstackdor lo aceptó y produjo esta salida:

 Int String String 

Ahora supongo que esto tiene que ver con el hecho de que Int no es un AnyRef, por lo que la única versión de f que funciona para f (null) es f (x: String). Pero luego, si un Int no puede ser nulo, ¿qué significa null.asInstanceOf [Int]? El repl dice que es del tipo Int:

 scala> :type null.asInstanceOf[Int] Int 

pero realmente no veo cómo funciona eso. Después de todo, si bash lanzar un String a un Int, todo el infierno se desata:

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

Por supuesto, es de esperar, “foo” no puede convertirse en un Int. Pero ninguno de los dos puede anular, entonces, ¿por qué lanzar nulo a un trabajo Int? Presumiblemente, el boxeo de alguna forma, pero el tipo sigue siendo Int, que no puede ser nulo …

¿Qué me estoy perdiendo?

El comportamiento de emitir null a un Int depende del contexto en el que se realiza.

En primer lugar, si lanzas un null a un Int , en realidad significa un entero encuadrado, cuyo valor es null . Si coloca la expresión en un contexto donde el tipo esperado es Any (que se traduce en Object detrás de la escena, porque en el código de byte JVM, no hay forma de referirse a un tipo primitivo y un tipo de referencia con la misma referencia), entonces este valor no se convierte aún más – es por eso que println(null.asInstanceOf[Int]) imprime null .

Sin embargo, si utiliza este mismo valor entero encuadrado en un contexto donde se espera una Int primitiva (Java int ), se convertirá en una primitiva, y null (como valor predeterminado para los tipos de referencia) convertida a 0 (valor predeterminado) valor para tipos primitivos).

Si un método genérico hace esto, entonces, naturalmente, obtienes un null vuelta.

Sin embargo, si este método es especializado, entonces su tipo de retorno es Int (que es un entero primitivo en este caso), por lo que el null: Any valor tiene que convertirse en un primitivo, como antes.

Por lo tanto, corriendo:

 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 

Aquí está la cosa: asInstanceOf no tiene que tener sentido. Lo que este método hace es decirle al comstackdor que DEJE DE TENER SENTIDO, y confíe en lo que está diciendo.

Ahora, si quiere saber por qué devuelve 0, eso es porque asInstanceOf funciona en AnyRef , no en AnyVal . Cuando se aplica a un AnyVal , utilizará la versión en caja en su lugar, y un null caja tiene el valor 0.

Parece que simplemente lo está convirtiendo automáticamente a cero:

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

Y, por supuesto, 0, a diferencia de null, puede ser un Int .

En primer lugar, todos estamos de acuerdo en que no podemos asignar null a scala.Int Como se documenta en http://www.scala-lang.org/api/current/index.html#scala.Null

En segundo lugar, ¿por qué cuando hacemos println(null.asInstanceOf[Int]) , da null ?
Esto se debe a la implementación de println. Eventualmente llama String.valueOf método java String.valueOf , que es

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

Si realiza null.asInstanceOf[Int] == null en el shell, devolverá true, pero dará una advertencia opuesta que “comparar valores de tipos Int y Null usando` == ‘siempre arrojará false “. Creo que esto podría ser un problema en el borrado de tipos de Scala.

Hacer una println solo necesita un scala. Cualquier tipo, por lo que el lanzamiento de null.asInstanceOf[Int] realidad no ha sucedido todavía. Así que solo debemos recordar que cuando asigna null.asInstanceOf[Int] a un Int, el reparto ocurre en el tiempo de ejecución en función de la semántica de borrado de Scala, y le asigna 0.

Por cierto, aún puedes hacer af (null) sin ningún error de comstackción porque scala está haciendo una conversión implícita para ti

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

Sin embargo, verá que explota en el tiempo de ejecución.