Encontré una situación en la que a un método no nulo le falta una declaración de devolución y el código aún se compila. Sé que las declaraciones después del ciclo while son inalcanzables (código muerto) y nunca se ejecutarán. Pero, ¿por qué el compilador ni siquiera advierte sobre devolver algo? ¿O por qué un lenguaje nos permitiría tener un método no vacío que tenga un bucle infinito y no devuelva nada?
public int doNotReturnAnything() {
while(true) {
//do something
}
//no return statement
}
Si agrego una declaración de interrupción (incluso una condicional) en el bucle while, el compilador se queja de los errores infames: Method does not return a value
en Eclipse y Not all code paths return a value
en Visual Studio.
public int doNotReturnAnything() {
while(true) {
if(mustReturn) break;
//do something
}
//no return statement
}
Esto es cierto tanto para Java como para C #.
unreachable statement
si hay algo después del ciclo.Respuestas:
La regla para los métodos no nulos es que cada ruta de código que devuelve debe devolver un valor , y esa regla se cumple en su programa: cero de cero rutas de código que devuelven devuelven un valor. La regla no es "todos los métodos no nulos deben tener una ruta de código que devuelva".
Esto le permite escribir métodos de código auxiliar como:
Ese es un método no vacío. Se tiene que ser un método no vacío con el fin de satisfacer la interfaz. Pero parece una tontería hacer que esta implementación sea ilegal porque no devuelve nada.
Que su método tenga un punto final inalcanzable debido a que
goto
(recuerde, awhile(true)
es solo una forma más agradable de escribirgoto
) en lugar de athrow
(que es otra forma degoto
) no es relevante.Porque el compilador no tiene buena evidencia de que el código esté equivocado. Alguien escribió
while(true)
y parece probable que la persona que hizo eso supiera lo que estaba haciendo.Vea mis artículos sobre el tema, aquí:
ATBG: accesibilidad de facto y de jure
Y también puede considerar leer la especificación de C #.
fuente
public int doNotReturnAnything() {
boolean flag = true;
while (flag) {
//Do something
}
//no return
}
Este código tampoco tiene un punto de interrupción. Entonces, ahora cómo el compilador sabe que el código está mal.if
,while
,for
,switch
, etc, construcciones de ramificación que operan sobre las constantes son tratados como ramas incondicionales por el compilador. La definición exacta de expresión constante está en la especificación. Las respuestas a sus preguntas están en la especificación; Mi consejo es que lo leas.Every code path that returns must return a value
La mejor respuesta. Aborda claramente ambas preguntas. GraciasEl compilador de Java es lo suficientemente inteligente como para encontrar el código inalcanzable (el código después del
while
bucle)y dado que es inalcanzable , no tiene sentido agregar una
return
declaración allí (después dewhile
finalizar)lo mismo va con condicional
if
dado que la condición booleana
someBoolean
solo puede evaluar a unotrue
u otrofalse
, no es necesario proporcionar unreturn
explícitamente despuésif-else
, porque ese código es inalcanzable y Java no se queja de ello.fuente
return
declaración en un código inalcanzable, pero no tiene nada que ver con por qué no necesita ningunareturn
declaración en el código del OP.public int doNotReturnAnything() {
if(true){
System.exit(1);
}
return 11;
}
public int doNotReturnAnything() {
while(true){
System.exit(1);
}
return 11;// Compiler error: unreachable code
}
System.exit(1)
matará al programa. Solo puede detectar ciertos tipos de código inalcanzable.System.exit(1)
que matará al programa, podemos usar cualquierareturn statement
, ahora el compilador reconoce elreturn statements
. Y el comportamiento es el mismo, el retorno requiereif condition
pero no parawhile
.while(true)
El compilador sabe que el
while
ciclo nunca dejará de ejecutarse, por lo tanto, el método nunca terminará, por lo tanto,return
no es necesaria una declaración.fuente
Dado que su ciclo se está ejecutando en una constante, el compilador sabe que es un ciclo infinito, lo que significa que el método nunca podría volver, de todos modos.
Si usa una variable, el compilador aplicará la regla:
Esto no compilará:
fuente
La especificación Java define un concepto llamado
Unreachable statements
. No está permitido tener una declaración inalcanzable en su código (es un error de tiempo de compilación). Ni siquiera se le permite tener una declaración de devolución después del tiempo (verdadero); declaración en Java. Unawhile(true);
declaración hace que las siguientes declaraciones sean inalcanzables por definición, por lo tanto, no necesita unareturn
declaración.Tenga en cuenta que si bien el problema de detención es indecidible en un caso genérico, la definición de declaración inalcanzable es más estricta que simplemente detenerla. Está decidiendo casos muy específicos en los que un programa definitivamente no se detiene. Teóricamente, el compilador no puede detectar todos los bucles infinitos y las declaraciones inalcanzables, pero tiene que detectar casos específicos definidos en la especificación (por ejemplo, el
while(true)
caso)fuente
El compilador es lo suficientemente inteligente como para descubrir que su
while
ciclo es infinito.Entonces el compilador no puede pensar por ti. No puedo adivinar por qué escribiste ese código. Lo mismo representa los valores de retorno de los métodos. Java no se quejará si no hace nada con los valores de retorno del método.
Entonces, para responder a su pregunta:
El compilador analiza su código y después de descubrir que ninguna ruta de ejecución conduce a la caída del final de la función, termina con OK.
Puede haber razones legítimas para un bucle infinito. Por ejemplo, muchas aplicaciones usan un bucle principal infinito. Otro ejemplo es un servidor web que puede esperar indefinidamente las solicitudes.
fuente
En la teoría de tipos, hay algo llamado tipo inferior que es una subclase de cualquier otro tipo (!) Y se usa para indicar la no terminación, entre otras cosas. (Las excepciones pueden contar como un tipo de no terminación: no termina a través de la ruta normal).
Entonces, desde una perspectiva teórica, se puede considerar que estas declaraciones que no terminan devuelven algo del tipo Bottom, que es un subtipo de int, por lo que, después de todo, obtiene su valor de retorno desde una perspectiva de tipo. Y está perfectamente bien que no tenga ningún sentido que un tipo pueda ser una subclase de todo lo demás, incluido int, porque en realidad nunca devuelve uno.
En cualquier caso, a través de la teoría explícita de tipos o no, los compiladores (escritores de compiladores) reconocen que pedir un valor de retorno después de una declaración sin terminación es una tontería: no hay ningún caso posible en el que pueda necesitar ese valor. (Puede ser agradable que su compilador le avise cuando sabe que algo no terminará pero parece que quiere que devuelva algo. Pero es mejor dejarlo para los correctores de estilo, ya que tal vez necesite la firma de tipo que por alguna otra razón (por ejemplo, subclases) pero realmente desea la no terminación).
fuente
No existe una situación en la que la función pueda llegar a su fin sin devolver un valor apropiado. Por lo tanto, el compilador no tiene nada de qué quejarse.
fuente
Visual Studio tiene el motor inteligente para detectar si ha escrito un tipo de retorno, entonces debe tener una declaración de retorno con la función / método.
Como en PHP, su tipo de devolución es verdadero si no ha devuelto nada. el compilador obtiene 1 si no se ha devuelto nada.
A partir de esto
El compilador sabe que, si bien la declaración en sí tiene una naturaleza infinita, no lo tenga en cuenta. y el compilador de php se volverá verdadero automáticamente si escribe una condición en expresión de while.
Pero no en el caso de VS, le devolverá un error en la pila.
fuente
Su ciclo while se ejecutará para siempre y, por lo tanto, no saldrá afuera mientras; continuará ejecutándose. Por lo tanto, la parte exterior de while {} es inalcanzable y no tiene sentido escribir return o no. El compilador es lo suficientemente inteligente como para descubrir qué parte es accesible y qué parte no.
Ejemplo:
El código anterior no se compilará, porque puede darse el caso de que el valor de la variable x se modifique dentro del cuerpo del ciclo while. ¡Esto hace que la parte exterior de while sea accesible! Y, por lo tanto, el compilador arrojará un error 'no se encontró ninguna declaración de retorno'.
El compilador no es lo suficientemente inteligente (o más bien vago;)) para determinar si el valor de x se modificará o no. Espero que esto aclare todo.
fuente
int x = 1; while(x * 0 == 0) { ... }
era un bucle infinito, pero la especificación dice que el compilador solo debe hacer deducciones de flujo de control cuando la expresión del bucle es constante , y la especificación define una expresión constante como que no contiene variables . El compilador, por lo tanto, era demasiado inteligente . En C # 3 hice que el compilador coincida con la especificación, y desde entonces es deliberadamente menos inteligente sobre este tipo de expresiones."¿Por qué el compilador ni siquiera advierte acerca de devolver algo? ¿O por qué un lenguaje nos permitiría tener un método no vacío que tenga un bucle infinito y no devuelva nada?".
Este código también es válido en todos los demás idiomas (¡probablemente excepto Haskell!). Porque la primera suposición es que estamos escribiendo "intencionalmente" algún código.
Y hay situaciones en las que este código puede ser totalmente válido, como si lo va a usar como hilo; o si estaba devolviendo a
Task<int>
, podría hacer alguna comprobación de errores en función del valor int devuelto, que no debería devolverse.fuente
Puedo estar equivocado, pero algunos depuradores permiten la modificación de variables. Aquí, mientras que x no está modificado por el código y JIT lo optimizará, uno podría modificar x a falso y el método debería devolver algo (si el depurador de C # lo permite).
fuente
Los detalles del caso de Java para esto (que probablemente son muy similares al caso de C #) tienen que ver con la forma en que el compilador de Java determina si un método puede regresar.
Específicamente, las reglas son que un método con un tipo de retorno no debe poder completarse normalmente y en su lugar siempre debe completarse abruptamente (abruptamente aquí indicando mediante una declaración de retorno o una excepción) según JLS 8.4.7 .
El compilador busca ver si la terminación normal es posible en función de las reglas definidas en JLS 14.21 Declaraciones inalcanzables, ya que también define las reglas para la finalización normal.
En particular, las reglas para las declaraciones inalcanzables hacen un caso especial solo para los bucles que tienen una
true
expresión constante definida :Entonces, si la
while
declaración puede completarse normalmente , entonces es necesaria una declaración de retorno a continuación, ya que el código se considera accesible, y cualquierwhile
ciclo sin una declaración de ruptura alcanzable otrue
expresión constante se considera capaz de completarse normalmente.Estas reglas significan que su
while
declaración con una verdadera expresión constante y sin unabreak
está nunca se consideró a completar normalmente , por lo que cualquier código de abajo se nunca se consideró para ser alcanzable . El final del método está debajo del ciclo, y dado que todo lo que está debajo del ciclo es inalcanzable, también lo es el final del método y, por lo tanto, el método no puede completarse normalmente (que es lo que busca el cumplidor).if
las declaraciones, por otro lado, no tienen la exención especial con respecto a las expresiones constantes que se otorgan a los bucles.Comparar:
Con:
La razón de la distinción es bastante interesante, y se debe al deseo de permitir marcas de compilación condicional que no causen errores de compilación (del JLS):
¿Por qué la declaración de interrupción condicional da como resultado un error del compilador?
Como se cita en las reglas de accesibilidad de bucle, un bucle while también puede completarse normalmente si contiene una declaración de interrupción alcanzable. Dado que las normas para la accesibilidad de una
if
de declaración a continuación cláusula de no tener la condición de laif
en consideración en absoluto, tal condicionalif
de la declaración a continuación, la cláusula se considera siempre localizable.Si
break
se puede acceder a él, el código después del ciclo también se considera accesible nuevamente. Dado que no hay un código accesible que dé como resultado una terminación abrupta después del ciclo, el método se considera capaz de completarse normalmente, por lo que el compilador lo marca como un error.fuente