É 1/0 uma expressão Java legal?

O seguinte compila bem no meu Eclipse:

final int j = 1/0; // compiles fine!!! // throws ArithmeticException: / by zero at run-time 

O Java impede que muitos “códigos burros” sejam compilados (por exemplo, "Five" instanceof Number não compila!), Então o fato de isso nem sequer gerar tanto quanto um aviso foi muito surpreendente para mim. A intriga se aprofunda quando você considera o fato de que expressões constantes podem ser otimizadas em tempo de compilation:

 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 no Eclipse, o snippet acima gera o seguinte 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."":()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 você pode ver, as atribuições i e k são otimizadas como constantes de tempo de compilation, mas a divisão por 0 (que deve ter sido detectada em tempo de compilation) é simplesmente compilada como está.

javac 1.6.0_17 se comporta ainda mais estranhamente, compilando silenciosamente, mas extendendo as atribuições para i e k completamente fora do bytecode (provavelmente porque determinou que eles não são usados ​​em qualquer lugar) mas deixando o 1/0 intacto (já que removê-lo causaria uma semântica de programa completamente diferente).

Então as perguntas são:

  • É 1/0 realmente uma expressão Java legal que deve compilar a qualquer hora em qualquer lugar?
    • O que o JLS diz sobre isso?
  • Se isso é legal, há uma boa razão para isso?
    • Que bom isso poderia servir?

1/0 é realmente uma expressão Java legal que deve ser compilada a qualquer hora em qualquer lugar?

Sim.

O que o JLS diz sobre isso?

Nada específico … além de dizer que a divisão por zero resultará em uma exceção de tempo de execução. No entanto, o JLS reconhece essa possibilidade de exceções de tempo de execução na seguinte definição:

“Uma expressão constante em tempo de compilation é uma expressão denotando um valor de tipo primitivo ou uma String que não é concluída abruptamente e é composta usando apenas o seguinte: …”

(Ênfase adicionada.) Assim, o seguinte NÃO compilaria:

 switch(i) { case 1: case 1 + 1: case 1 / 0: // compilation error. } 

Se isso é legal, há uma boa razão para isso?

Boa pergunta. Suponho que seja uma maneira de lançar a exceção ArithmeticException embora essa não seja uma razão plausível. Uma razão mais provável para especificar o Java dessa maneira é evitar complexidade desnecessária no JLS e nos compiladores para lidar com um caso extremo que raramente vai incomodar as pessoas.

Mas isso é tudo por perto. O fato é que 1/0 é um código Java válido e nenhum compilador Java deve sinalizar isso como um erro de compilation. (Seria razoável que um compilador Java emitisse um aviso, desde que houvesse uma opção do compilador para desligá-lo.)

Eu investiguei o database de bugs e descobri algumas informações interessantes.

ID do erro 4178182: O JLS não especifica o comportamento de 1/0 como uma expressão constante

O código a seguir é ilegal:

 class X { static final int i = 1 / 0; } 

O valor dessa constante de tempo de compilation é indefinido, portanto , isso deve ser um erro de tempo de compilation. Guy Steele confirmou há cerca de 18 meses que este era realmente o comportamento pretendido.

Uma constante de tempo de compilation tem que ter seu valor disponível estaticamente (é o que faz dela uma constante de tempo de compilation 😉 Por exemplo, o valor de outras constantes cujos valores são determinados por uma constante que contém uma divisão por zero é indefinido. Isso afeta a semântica das instruções switch , a atribuição e a atribuição definitivas, etc.

ID do erro: 4089107: o javac trata a divisão inteira por (constante) zero como um erro

 public class zero { public static void main(String[] args) { System.out.println(1/0); } } 

Executando os rendimentos acima:

 zero.java:3: Arithmetic exception. System.out.println(1/0); ^ 1 error 

ID do erro 4154563: o javac aceita divisão por expressões constantes zero em expressões de caso.

O compilador Java falha ao tentar compilar o próximo teste. Este teste também trava todas as versões do compilador 1.2beta4, mas o bug está ausente em 12.beta3. Um exemplo e diagnóstico do compilador são os seguintes:

 public class B { public static void main(String argv[]) { switch(0){ case 0/0: } } } 

Avaliação: O compilador usado para relatar todas as tentativas de dividir pela constante zero como erros de tempo de compilation. Isso foi corrigido em beta3 para que o código fosse gerado para divisão por zero constante. Infelizmente este bug foi introduzido. O compilador deve manipular uma divisão por zero em uma expressão de caso normalmente.

Conclusão

Então a questão de se o 1/0 deveria ou não compilar era um tópico controverso de discussão, com algumas pessoas citando Guy Steele alegando que isso deveria ser um erro de compilation, e outras dizendo que não deveria. Parece que, em última análise, é decidido que não é um erro de tempo de compilation nem uma constante de tempo de compilation.

Java requer explicitamente divisão inteira por zero para acionar uma ArithmeticException . A atribuição de j não pode ser eliminada porque isso violaria a especificação.

Bem, se você olhar para a class Double, verá o seguinte:

 /** * A constant holding the positive infinity of type * double. It is equal to the value returned by * Double.longBitsToDouble(0x7ff0000000000000L). */ public static final double POSITIVE_INFINITY = 1.0 / 0.0; 

O mesmo cálculo é feito na class Float, exceto com floats ao invés de duplas. Basicamente, 1/0 retorna um número realmente grande, maior que Double.MAX_VALUE.

Este código a seguir:

 public static void main(String[] args) { System.out.println(Double.POSITIVE_INFINITY); System.out.println(Double.POSITIVE_INFINITY > Double.MAX_VALUE); } 

Saídas:

 Infinity true 

Observe o caso especial na impressão de Double.POSITIVE_INFINITY . Imprime uma corda, embora seja considerada como um duplo.

Para responder à pergunta, sim, é legal em Java, mas 1/0 resolve “infinito” e é tratado de maneira diferente dos duplos padrão (ou flutuantes, e assim por diante).

Devo notar que não tenho a menor idéia de como ou por que foi implementado dessa maneira. Quando vejo a saída acima, tudo parece magia negra para mim.

É legal porque não é um dado que o compilador deve dobrar expressões constantes em tempo de compilation.

Um compilador “inteligente” pode compilar:

 a = 1 + 2 

Como

 a = 3 

Mas não há nada que diga que o compilador tem que fazer isso. Fora isso, 1/0 é uma expressão legal como:

 int a; int b; a = a/b; 

é uma expressão legal.

No RUNTIME, ele lança uma exceção, mas é um erro de execução por um motivo.

Por que se preocupar em pegar isso em tempo de compilation, quando você vai precisar de uma variante de tempo de execução de qualquer maneira?

Por exemplo, se você estivesse carregando e analisando “0” de um arquivo de texto e tentasse dividir por ele, o Java não teria ideia do que estava fazendo em tempo de compilation, porque não conhece o conteúdo desse arquivo externo.

Além disso, se você tivesse que definir qualquer variável como 0 e dividir pela variável, o Java teria que acompanhar todos os valores possíveis de cada variável em cada ponto do script, a fim de capturar uma divisão por 0 no tempo de compilation.

Pode também manter as coisas consistentes e torná-lo uma exceção apenas em tempo de execução.

É legal na compilation do ponto de vista , mas lançaria uma exceção se executada!

o motivo … bem programação deve permitir flexibilidade, portanto, todas as expressões e cada código que você digita é uma variável para o compilador, assim, na expressão matemática X / Y o compilador não se importa se o valor da variável Y é ( Y == 0 ) ou qualquer outro número para o compilador esta é uma variável … se o compilador teria que olhar para os valores também, isso seria considerado tempo de execução, não seria?

Como outros já responderam à legalidade do 1/0 , vamos para a segunda questão:

  • Se isso é legal, há uma boa razão para isso?
    • Que bom isso poderia servir?

Uma resposta poderia ser:

Para provocar um colega seu. o)

Quando o colega sair da sala com o computador deixado desbloqueado, esgueirar-se e enterrar 1/0 algum lugar no fundo de um inicializador estático de alguma class usada no início do aplicativo. Dessa forma, ele descobrirá logo após (ou mesmo durante) a implantação do aplicativo, encontrando a Exceção ArithmeticException incomum e provavelmente irá coçar a cabeça por algum tempo. Usando essa maneira rápida, você pode garantir que seja uma piada relativamente inofensiva.

PS: Funcionou. o)