oxygen - ¿Error de código inalcanzable vs. advertencia de código muerto en Java bajo Eclipse?
windowbuilder eclipse tutorial (8)
Alguien sabe por qué:
public void foo()
{
System.out.println("Hello");
return;
System.out.println("World!");
}
Se informaría como un "error inalcanzable" en Eclipse, pero
public void foo()
{
System.out.println("Hello");
if(true) return;
System.out.println("World!");
}
¿Solo se activa una advertencia de "Código muerto"?
La única explicación que se me ocurre es que el compilador de Java solo marca el primero, y que un análisis adicional en Eclipse resuelve el segundo. Sin embargo, si ese es el caso, ¿por qué el compilador de Java no puede resolver este caso en tiempo de compilación?
¿No se daría cuenta el compilador de Java en el momento de la compilación que if (true) no tiene ningún efecto, por lo que produce un código de byte que es esencialmente idéntico? ¿En qué punto se aplica el análisis de código alcanzable?
Supongo que una forma más general de pensar esta pregunta es: "¿cuándo se aplica el análisis de código alcanzable"? En la transformación del segundo fragmento de código de Java al código de byte final, estoy seguro de que en algún momento se elimina el equivalente de tiempo de ejecución "if (true)", y las representaciones de los dos programas se vuelven idénticas. ¿El compilador de Java no aplicaría de nuevo el análisis de código accesible?
Creo que una forma de hacerlo es que el código inalcanzable es probablemente un error, y el JLS intenta protegerlo de tales errores.
Permitiendo if (true) return;
es una buena manera de evitar la limitación de JLS si realmente desea hacerlo a propósito. Si el JLS detuviera esto, estaría estorbando. Además, también debe dejar de:
public static boolean DEBUG = true; //In some global class somewhere else
...
if (DEBUG) return; //in a completely unrelated class.
...
Debido a que la constante DEBUG está completamente alineada, y es equivalente funcionalmente a solo escribir un verdadero en esa condición if. Desde una perspectiva JLS esos dos casos son muy similares.
El if (true)
es un poco más sutil que "inalcanzable"; porque ese return
codificado siempre hará que el siguiente código sea inalcanzable, pero cambiar la condición en el if
podría hacer que la siguiente declaración sea accesible.
Tener un condicional allí significa que hay una condición que podría cambiar. Hay casos en los que hay algo más complicado que una true
entre paréntesis, y no es obvio para el lector humano que el siguiente código está "muerto", pero el compilador lo advierte, por lo que es capaz de advertirle al respecto.
Eclipse se menciona aquí, y hace que las cosas parezcan un poco más complicadas para el usuario; pero en realidad, debajo de Eclipse hay un compilador Java (muy sofisticado) que incluye muchos interruptores para advertencias, etc. que Eclipse puede activar y desactivar. En otras palabras, no se obtiene la amplitud de diferentes advertencias / errores de una compilación javac
directa, ni tiene medios convenientes para activarlos o desactivarlos. Pero es el mismo trato, solo que con más campanas y silbidos.
El código inalcanzable es un error de acuerdo con la especificación del lenguaje Java .
Para citar desde el JLS:
La idea es que debe haber alguna ruta de ejecución posible desde el principio del constructor, el método, el inicializador de instancia o el inicializador estático que contenga la declaración a la declaración en sí. El análisis tiene en cuenta la estructura de las declaraciones. Excepto por el tratamiento especial de while, do, y para las declaraciones cuya expresión de condición tiene el valor constante verdadero, los valores de las expresiones no se tienen en cuenta en el análisis de flujo.
Lo que eso significa es que el bloque if
no se tiene en cuenta, ya que si atraviesas una de las rutas de la sentencia if
, podrías llegar a la declaración de impresión final. Si cambiaste tu código para ser:
public void foo() {
System.out.println("Hello");
if (true)
return;
else
return;
System.out.println("World!");
}
luego, de repente, ya no se compilaría, ya que no hay una ruta a través de la instrucción if
que permita alcanzar la última línea.
Es decir, un compilador compatible con Java no puede compilar su primer fragmento de código. Para citar más a fondo el JLS:
Como ejemplo, la siguiente declaración da como resultado un error en tiempo de compilación:
while (false) { x=3; }
porque la declaración x = 3; no es alcanzable; Pero el caso superficialmente similar:
if (false) { x=3; }
no da lugar a un error en tiempo de compilación. Un compilador de optimización puede darse cuenta de que la instrucción x = 3; nunca se ejecutará y puede optar por omitir el código para esa declaración del archivo de clase generado, pero la instrucción x = 3; no se considera "inalcanzable" en el sentido técnico especificado aquí.
La segunda advertencia que Eclipse da, acerca del código muerto, es una advertencia generada por el compilador, que no es "inalcanzable", según el JLS, pero en la práctica es. Este es un control de estilo de lint adicional que Eclipse proporciona. Esto es completamente opcional y, al utilizar la configuración de Eclipse, se puede desactivar o convertir en un error de compilación en lugar de una advertencia.
Este segundo bloque es un "olor a código", if (false)
bloques if (false)
normalmente se colocan para deshabilitar el código con fines de depuración, dejarlo atrás es generalmente accidental y, por lo tanto, es una advertencia.
De hecho, Eclipse realiza pruebas aún más avanzadas para determinar los valores posibles de una instrucción if para determinar si es posible o no tomar ambas rutas. Por ejemplo, Eclipse también se quejaría sobre el código muerto en el siguiente método:
public void foo() {
System.out.println("Hello");
boolean bool = Random.nextBoolean();
if (bool)
return;
if (bool || Random.nextBoolean())
System.out.println("World!");
}
Generará un código inalcanzable para la segunda instrucción if, ya que puede razonar que bool
solo debe ser false
en este punto del código. En un fragmento de código tan corto, es obvio que las dos afirmaciones if están probando lo mismo, sin embargo, si hay entre 10 y 15 líneas de código en el medio, puede que ya no sea tan obvio.
Entonces, en resumen, la diferencia entre los dos: uno está prohibido por el JLS y el otro no, pero Eclipse lo detecta como un servicio para el programador.
El primero no se compila (obtuviste un error), el segundo compila (solo recibiste una advertencia). Esa es la diferencia.
En cuanto a por qué Eclipse detecta el código muerto, bueno, eso es solo la conveniencia de una herramienta de desarrollo integrada con un compilador incorporado que se puede perfeccionar más a diferencia del JDK para detectar este tipo de código.
Actualización : el JDK en realidad elimina el código muerto.
public class Test {
public void foo() {
System.out.println("foo");
if(true)return;
System.out.println("foo");
}
public void bar() {
System.out.println("bar");
if(false)return;
System.out.println("bar");
}
}
javap -c
dice:
public class Test extends java.lang.Object{ public Test(); Code: 0: aload_0 1: invokespecial #1; //Method java/lang/Object."":()V 4: return public void foo(); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #3; //String foo 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/StrV 8: return public void bar(); Code: 0: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc #5; //String bar 5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: getstatic #2; //Field java/lang/System.out:Ljava/io/PrintStream; 11: ldc #5; //String bar 13: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 16: return }
En cuanto a por qué (Sun) no da una advertencia al respecto, no tengo idea :) Al menos el compilador JDK tiene realmente DCE (Eliminación de Código Muerto) incorporado.
Esto es para permitir una especie de compilación condicional .
No es un error con if
, pero el compilador marcará un error para while
, do-while
y for
.
Esto esta bien:
if (true) return; // or false
System.out.println("doing something");
Esto son errores
while (true) {
}
System.out.println("unreachable");
while (false) {
System.out.println("unreachable");
}
do {
} while (true);
System.out.println("unreachable");
for(;;) {
}
System.out.println("unreachable");
Se explica al final de JLS 14.21: Declaraciones inalcanzables :
El fundamento de este tratamiento diferente es permitir que los programadores definan "variables de marca" como:
static final boolean DEBUG = false;
y luego escribir código como:
if (DEBUG) { x=3; }
La idea es que debería ser posible cambiar el valor de DEBUG de falso a verdadero o de verdadero a falso y luego compilar el código correctamente sin otros cambios en el texto del programa.
Hice algunos intentos de eclipse y creo que hay 3 tipos de códigos muertos de manejo de JDK: 1) no advertir, 2) advertir y 3) error.
Para un código de compilación condicional típico "IF", JDK detecta esto y no lo reporta como código muerto. Para un código muerto causado por un indicador booleano constante, JDK lo detecta y lo informa en el nivel de advertencia. Para el código muerto que es la causa del flujo de control del programa, JDK lo detecta como error.
A continuación es mi intento:
public class Setting {
public static final boolean FianlDebugFlag = false;
}
class B {
.....
// no warn, it is typical "IF" conditional compilataion code
if(Setting.FianlDebugFlag)
System.out.println("am i dead?");
if(false)
System.out.println("am i dead?");
// warn, as the dead code is caused by a constant boolean flag
if(ret!=null && Setting.FianlDebugFlag)
System.out.println("am i dead?");
if(Setting.FinalDebug)
return null;
System.out.println("am i dea?");
// error, as the dead code is due to the program''s control flow
return null;
System.out.println("am i dead");
}
La diferencia está en la semántica entre el tiempo de ejecución y el tiempo de compilación. En su segundo ejemplo, el código se compila en una rama if-else en el bytecode, y eclipse es lo suficientemente inteligente como para decirle que la parte else nunca se alcanzará en tiempo de ejecución. Eclipse solo te advierte, porque sigue siendo un código legal.
En su primer ejemplo, es un error porque el código es ilegal según la definición de java. El compilador no le permite crear un código de bytes con declaraciones inalcanzables.
Si desea ignorar la advertencia "advertencia de código muerto en Java bajo Eclipse", haga lo siguiente dentro de eclipse *:
- Haga clic en Ventana-Preferencias-Java-Compilador-Errores / Advertencias
- Haga clic en "Problemas potenciales de programación"
- Elija "Ignorar" el "Código Muerto, por ejemplo, si (falso)"
- Haga clic en Aplicar
- Haga clic en Aceptar
Guarde y cierre su IDE de eclipse. Cuando vuelva a abrir eclipse, estas advertencias específicas ya no deberían aparecer en la lista.
* Para esta solución de ejemplo, estoy usando Eclipse IDE para desarrolladores de Java - Versión: Mars.2 Release (4.5.2)