Pregunta 1:
¿Por qué se compila el siguiente código sin tener una declaración de retorno?
public int a() {
while(true);
}
Aviso: si agrego return después de un tiempo, obtengo un Unreachable Code Error
.
Pregunta 2:
Por otro lado, ¿por qué se compila el siguiente código?
public int a() {
while(0 == 0);
}
aunque lo siguiente no.
public int a(int b) {
while(b == b);
}
java
syntax
while-loop
compilation
return
Willi Mentzel
fuente
fuente
Respuestas:
Esto está cubierto por JLS§8.4.7 :
Como el compilador sabe que el ciclo nunca terminará (
true
siempre es cierto, por supuesto), sabe que la función no puede "regresar normalmente" (dejar el extremo de su cuerpo), y por lo tanto está bien que no hayareturn
.En el
0 == 0
caso, el compilador sabe que el ciclo nunca terminará (eso0 == 0
siempre será cierto). Pero no lo sabe porb == b
.Por qué no?
El compilador comprende expresiones constantes (§15.28) . Citando §15.2 - Formas de expresiones (porque curiosamente esta oración no está en §15.28) :
En su
b == b
ejemplo, debido a que hay una variable involucrada, no es una expresión constante y no se especifica que se determine en el momento de la compilación. Podemos ver que siempre será cierto en este caso (aunque sib
fuera undouble
, como señaló QBrute , podríamos engañarnos fácilmenteDouble.NaN
, lo que no==
es en sí mismo ), pero el JLS solo especifica que las expresiones constantes se determinan en el momento de la compilación , no permite que el compilador intente evaluar expresiones no constantes. bayou.io planteó un buen punto para por qué no: si comienza a tratar de determinar expresiones que involucran variables en el momento de la compilación, ¿dónde se detiene?b == b
es obvio (er, para noNaN
valores), pero ¿qué pasaa + b == b + a
? O(a + b) * 2 == a * 2 + b * 2
? Dibujar la línea en las constantes tiene sentido.Por lo tanto, dado que no "determina" la expresión, el compilador no sabe que el ciclo nunca terminará, por lo que cree que el método puede regresar normalmente, lo que no está permitido, porque es obligatorio
return
. Entonces se queja de la falta de areturn
.fuente
Puede ser interesante pensar en un tipo de retorno de método no como una promesa de devolver un valor del tipo especificado, sino como una promesa de no devolver un valor que no sea del tipo especificado. Por lo tanto, si nunca devuelve nada, no está incumpliendo la promesa, por lo que cualquiera de los siguientes es legal:
Bucle para siempre:
Recurrente para siempre:
Tirando una excepción:
(Me parece divertido pensar en la recursividad: el compilador cree que el método devolverá un valor de tipo
X
(sea lo que sea), pero no es cierto, porque no hay código presente que tenga alguna idea de cómo crear o procurar unX
.)fuente
Si observa el código de bytes, si lo que se devuelve no coincide con la definición, recibirá un error de compilación.
Ejemplo:
for(;;)
mostrará los bytecodes:Tenga en cuenta la falta de código de bytes de retorno
Esto nunca llega a un retorno y, por lo tanto, no devuelve el tipo incorrecto.
A modo de comparación, un método como:
Devolverá los siguientes códigos de bytes:
Tenga en cuenta el "areturn" que significa "devolver una referencia"
Ahora si hacemos lo siguiente:
Devolverá los siguientes códigos de bytes:
Ahora podemos ver que el tipo en la definición no coincide con el tipo de retorno de ireturn, lo que significa retorno int.
Así que realmente se reduce a que si el método tiene una ruta de retorno, esa ruta debe coincidir con el tipo de retorno. Pero hay instancias en el código de bytes en las que no se genera ninguna ruta de retorno y, por lo tanto, no se rompe la regla.
fuente