valores tipos sintaxis sentencia retornar retornan que metodos metodo java syntax while-loop compilation return

sintaxis - tipos de metodos en java



Método Java con compilaciones de tipo de retorno sin declaración de retorno (3)

Pregunta 1:

¿Por qué se compila el siguiente código sin tener una declaración de devolución?

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


Pregunta 1:

¿Por qué se compila el siguiente código sin tener una declaración de devolución?

public int a() { while(true); }

Esto está cubierto por JLS§8.4.7 :

Si se declara que un método tiene un tipo de retorno (§8.4.5), se produce un error en tiempo de compilación si el cuerpo del método puede completarse normalmente (§14.1).

En otras palabras, un método con un tipo de retorno debe devolver solo mediante el uso de una declaración de retorno que proporciona un valor de retorno; el método no puede "dejar el extremo de su cuerpo". Vea §14.17 para las reglas precisas sobre las declaraciones de retorno en un cuerpo de método.

Es posible que un método tenga un tipo de retorno y, sin embargo, no contenga declaraciones de retorno. Aquí hay un ejemplo:

class DizzyDean { int pitch() { throw new RuntimeException("90 mph?!"); } }

Dado que el compilador sabe que el ciclo nunca terminará ( true es siempre 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 haya return .

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

En el caso 0 == 0 , el compilador sabe que el ciclo nunca terminará (que 0 == 0 siempre será verdadero). Pero no sabe eso para b == 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) :

Algunas expresiones tienen un valor que se puede determinar en tiempo de compilación. Estas son expresiones constantes (§15.28).

En su ejemplo b == b , 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 si b fuera un double , como señaló Double.NaN , Double.NaN , que no es == sí mismo , podría Double.NaN fácilmente), pero el JLS solo especifica que las expresiones constantes se determinan en tiempo de 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 valores que no son NaN ), pero ¿qué pasa con a + b == b + a ? ¿O (a + b) * 2 == a * 2 + b * 2 ? Dibujar la línea en las constantes tiene sentido.

Entonces, 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 hacer, porque se requiere usar return . Entonces se queja de la falta de un return .


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:

  1. Bucle para siempre:

    X foo() { for (;;); }

  2. Recurrente para siempre:

    X foo() { return foo(); }

  3. Tirando una excepción:

    X foo() { throw new Error(); }

(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 una X )


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:

L0 LINENUMBER 6 L0 FRAME SAME GOTO L0

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:

public String getBar() { return bar; }

Devolverá los siguientes códigos de bytes:

public java.lang.String getBar(); Code: 0: aload_0 1: getfield #2; //Field bar:Ljava/lang/String; 4: areturn

Tenga en cuenta el "areturn" que significa "devolver una referencia"

Ahora si hacemos lo siguiente:

public String getBar() { return 1; }

Devolverá los siguientes códigos de bytes:

public String getBar(); Code: 0: iconst_1 1: ireturn

Ahora podemos ver que el tipo en la definición no coincide con el tipo de retorno de ireturn, que significa return int.

Entonces, realmente, todo 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.