¿Es 1/0 una expresión legal de Java?
divide-by-zero (8)
Lo siguiente compila bien en mi Eclipse:
final int j = 1/0;
// compiles fine!!!
// throws ArithmeticException: / by zero at run-time
Java previene que muchos "códigos tontos" incluso compilen en primer lugar (por ejemplo, "Five" instanceof Number
no compila!), Así que el hecho de que esto ni siquiera generara tanto como una advertencia fue muy sorprendente para mí. La intriga se profundiza cuando se tiene en cuenta el hecho de que las expresiones constantes pueden optimizarse en tiempo de compilación:
public class Div0 {
public static void main(String[] args) {
final int i = 2+3;
final int j = 1/0;
final int k = 9/2;
}
}
Compilado en Eclipse, el fragmento de arriba genera el siguiente bytecode ( javap -c Div0
)
Compiled from "Div0.java"
public class Div0 extends java.lang.Object{
public Div0();
Code:
0: aload_0
1: invokespecial #8; //Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_5
1: istore_1 // "i = 5;"
2: iconst_1
3: iconst_0
4: idiv
5: istore_2 // "j = 1/0;"
6: iconst_4
7: istore_3 // "k = 4;"
8: return
}
Como puede ver, las asignaciones i
y k
están optimizadas como constantes de tiempo de compilación, pero la división por 0
(que debe haber sido detectable en tiempo de compilación) simplemente se compila tal como está.
javac 1.6.0_17
comporta de forma aún más extraña, compilando silenciosamente pero eliminando completamente las asignaciones de i
y k
del bytecode (probablemente porque determinó que no se usan en ninguna parte) pero dejando intacto el 1/0
(ya que eliminarlo causaría una semántica de programa completamente diferente).
Entonces las preguntas son:
- ¿Es
1/0
realmente una expresión legal de Java que debe compilarse en cualquier momento en cualquier lugar?- ¿Qué dice JLS al respecto?
- Si esto es legal, ¿hay una buena razón para ello?
- ¿Qué bien podría servir esto?
¿Es
1/0
realmente una expresión legal de Java que debe compilarse en cualquier momento en cualquier lugar?
Sí.
¿Qué dice JLS al respecto?
Nada específico ... aparte de decir que la división por cero resultará en una excepción de tiempo de ejecución. Sin embargo, el JLS reconoce la posibilidad de excepciones de tiempo de ejecución en la siguiente definición:
"Una expresión constante en tiempo de compilación es una expresión que denota un valor de tipo primitivo o una cadena que no se completa abruptamente y se compone solo con lo siguiente: ..."
(Énfasis añadido). Por lo tanto, NO compilaría lo siguiente:
switch(i) {
case 1:
case 1 + 1:
case 1 / 0: // compilation error.
}
Si esto es legal, ¿hay una buena razón para ello?
Buena pregunta. Supongo que es una forma de lanzar ArithmeticException
aunque esa no es una razón plausible. Una razón más probable para especificar Java de esta manera es evitar la complejidad innecesaria en el JLS y los compiladores para manejar un caso extremo que rara vez va a morder a las personas.
Pero esto es todo por el estilo. El hecho es que 1/0
es un código Java válido, y ningún compilador Java debería marcar esto como un error de compilación. (Sería razonable que un compilador de Java emitiera una advertencia, siempre que hubiera un interruptor de compilación para desactivarlo).
¡Es legal en el punto de vista de compilación , pero arrojaría una excepción si se ejecutara!
la razón ... bueno, la programación debe permitir flexibilidad, por lo tanto, todas las expresiones y cada código que escriba son variables para el compilador, por lo tanto, en la expresión matemática X / Y al compilador no le importa si el valor de la variable Y es ( Y == 0 ) o cualquier otro número para el compilador esta es una variable ... si el compilador tuviera que mirar también los valores, eso se consideraría tiempo de ejecución, ¿no es así?
¿Por qué molestarse en capturar esto en tiempo de compilación, cuando va a necesitar una variante de tiempo de ejecución de todos modos?
Por ejemplo, si estuviera cargando y analizando "0" desde un archivo de texto, y luego intentado dividirlo, Java no tendría idea de lo que estaba haciendo en tiempo de compilación porque no conoce el contenido de ese archivo externo.
Además, si establecieras cualquier variable a 0 y dividieras por la variable, Java debería realizar un seguimiento de cada valor posible de cada variable en cada punto del script para poder dividir por 0 en el momento de la compilación.
También podría mantener las cosas consistentes y convertirlo en una excepción solo de tiempo de ejecución.
Bueno, si observas la clase Doble, verás lo siguiente:
/**
* A constant holding the positive infinity of type
* <code>double</code>. It is equal to the value returned by
* <code>Double.longBitsToDouble(0x7ff0000000000000L)</code>.
*/
public static final double POSITIVE_INFINITY = 1.0 / 0.0;
El mismo cálculo se realiza en la clase Float, excepto con flotantes en lugar de dobles. Básicamente, 1/0 devuelve un número muy, muy grande, más grande que Double.MAX_VALUE.
Este siguiente código:
public static void main(String[] args) {
System.out.println(Double.POSITIVE_INFINITY);
System.out.println(Double.POSITIVE_INFINITY > Double.MAX_VALUE);
}
Productos:
Infinity
true
Tenga en cuenta el caso especial al imprimir Double.POSITIVE_INFINITY
. Imprime una cuerda, aunque se considera como un doble.
Para responder la pregunta, sí, es legal en Java, pero 1/0 resuelve "infinito" y se trata de manera diferente a los Dobles estándar (o flotantes, etc.).
Debo señalar que no tengo la menor idea de cómo o por qué se implementó de esta manera. Cuando veo el resultado anterior, todo me parece magia negra.
Como otros ya respondieron la legalidad de 1/0
, 1/0
a la segunda pregunta:
- Si esto es legal, ¿hay una buena razón para ello?
- ¿Qué bien podría servir esto?
Una respuesta podría ser:
Para burlar a un colega tuyo. ; o)
Cuando el colega abandona la sala con su computadora desbloqueada, escabullirse y enterrar
1/0
algún lugar profundo en un inicializador estático de alguna clase que se usa al principio de la aplicación. De esta forma, pronto lo descubrirá (o incluso durante) el despliegue de la aplicación encontrando la inusualArithmeticException
y probablemente se rasque la cabeza por un tiempo. Usando esta manera rápida, puede asegurarse de que sea una broma relativamente inofensiva.
PD: funcionó. ; o)
Es legal porque no se sabe dónde se supone que el compilador debe doblar expresiones constantes en tiempo de compilación.
Un compilador "inteligente" podría compilar:
a = 1 + 2
como
a = 3
Pero no hay nada que diga que el compilador TIENE que hacer eso. Aparte de eso, 1/0 es una expresión legal como la siguiente:
int a;
int b;
a = a/b;
es una expresión legal
En RUNTIME arroja una excepción, pero eso es un error de tiempo de ejecución por una razón.
Indagué en la Base de datos de errores y descubrí información interesante.
Error ID 4178182: JLS no especifica el comportamiento para 1/0 como una expresión constante
El siguiente código es ilegal:
class X { static final int i = 1 / 0; }
El valor de esta constante en tiempo de compilación no está definido, por lo tanto , este debe ser un error en tiempo de compilación. Guy Steele confirmó hace unos 18 meses que este era de hecho el comportamiento previsto.
Una constante en tiempo de compilación tiene que tener su valor disponible estáticamente (eso es lo que lo hace una constante en tiempo de compilación ;-) Por ejemplo, el valor de otras constantes cuyos valores están determinados por una constante que contiene una división por cero no están definidos. Esto afecta la semántica de las declaraciones de
switch
, asignación y desasignación definidas, etc.
Error ID 4089107: javac trata la división de enteros por cero (constante) como un error
public class zero { public static void main(String[] args) { System.out.println(1/0); } }
Ejecutando los rendimientos anteriores:
zero.java:3: Arithmetic exception. System.out.println(1/0); ^ 1 error
Error ID 4154563: javac acepta la división por expresiones constantes cero en expresiones de casos.
El compilador de Java falla al intentar compilar la próxima prueba. Esta prueba también bloquea todas las versiones del compilador de 1.2beta4, pero el error está ausente en 12.beta3. Un ejemplo y el diagnóstico del compilador siguen:
public class B { public static void main(String argv[]) { switch(0){ case 0/0: } } }
Evaluación: el compilador solía informar todos los intentos de dividir por el cero constante como errores en tiempo de compilación. Esto se arregló en beta3 para que el código se genere para la división por cero constante. Lamentablemente, este error fue introducido. El compilador debe manejar una división por cero en una expresión de caso con gracia.
Conclusión
Entonces, la cuestión de si debería compilarse o no 1/0
era un tema de discusión controvertido, con algunas personas citando a Guy Steele alegando que esto debería ser un error de tiempo de compilación, y otros diciendo que no debería. Parece que, en última instancia, se decidió que no es ni un error en tiempo de compilación ni una constante en tiempo de compilación.
Java requiere explícitamente la división de enteros por cero para activar una ArithmeticException
. La asignación a j
no puede ser eliminada porque eso violaría la especificación.