2012-05-25 13 views
30

Como prueba, he escrito este código:Si un Int no puede ser nulo, ¿qué 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) 
} 

me esperaba conseguir un error en la última invocación de f(), diciendo que era ambigua. El compilador lo aceptó, y produjo este resultado:

Int 
String 
String 

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

scala> :type null.asInstanceOf[Int] 
Int 

pero yo no veo la forma en que funciona. Después de todo, si trato de convertir una cadena a un entero, 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 que es de esperar - "foo" no se puede convertir en un Int. Pero ninguno de los dos puede anular, entonces, ¿por qué lanzar nulo a un trabajo Int? Presumiblemente boxeo de alguna forma, pero el tipo sigue siendo Int, que no puede ser nulo ...

¿Qué me estoy perdiendo?

+0

Ah, y ¿cómo podría haber llegado a entender esto por mi cuenta, usando REPL o scalap o algún indicador de compilador mágico o ...? – AmigoNico

+0

Otro similar [pregunta] (http://stackoverflow.com/q/8285916/617996) ... – PrimosK

+0

Eche un vistazo a una actualización reciente de las especificaciones https://github.com/scala/scala-dist/pull/20 y emita https://issues.scala-lang.org/browse/SI-4437, que explica algunos de los antecedentes. Es un poco comparable a 'default (T)' en C#, afaik. – soc

Respuesta

46

El comportamiento de la conversión de null a Int depende del contexto en el que se realiza.

En primer lugar, si lanzas una null a un Int, que en realidad significa un número entero en caja, cuyo valor es null. Si coloca la expresión en un contexto donde el tipo esperado es Any (que se traduce a Object detrás de la escena, porque en el bytecode de 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 .

Sin embargo, si se utiliza este mismo valor entero en caja en un contexto en el que se espera una primitiva Int (Java int), que se convertirá en una primitiva, y null es (como un valor por defecto para los tipos de referencia) se convierta en 0 (un valor predeterminado para tipos primitivos).

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

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

Por lo tanto, en funcionamiento:

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

Detalles muy útiles - ¡gracias! – AmigoNico

+0

No me di cuenta que Int es un tipo primitivo – zinking

4

Parece que acaba de convertir automáticamente a cero:

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

Y, por supuesto, 0, a diferencia nula, puede ser una Int.

+0

Muy interesante. Me avergüenza que, mientras le pedía a la réplica el * tipo * de null.asInstanceOf [Int], nunca se me ocurrió preguntarle por su * valor *, que tontamente supuse que era nulo. ¡Gracias! Sin embargo, esta conversión me sorprende un poco, hmmm. – AmigoNico

+1

También me sorprende. No tengo idea si es el comportamiento previsto, pero me parece incorrecto. – dhg

+1

Este es el comportamiento previsto: obtienes el mismo valor de 'var x: X = _' que de' var x = null.asInstanceOf [X] 'para cualquier' X'. –

20

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

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

+0

Muy claro y conciso - ¡gracias! – AmigoNico

0

En primer lugar, todos estamos de acuerdo 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. Con el tiempo, llama al método Java String.valueOf, que es

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

Si lo hace un null.asInstanceOf[Int] == null en la cáscara, se volverá verdad, pero da una advertencia opuesta que "la comparación de valores de tipos Int y nulo el uso de` ==' siempre dará como resultado falso ". Creo que esto podría ser un problema en el borrado de tipos de Scala.

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

Por cierto, todavía se puede hacer una f (nulo) sin ningún error de compilación porque Scala está haciendo una conversión implícita de que

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

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

Cuestiones relacionadas