Por que Java tem um erro de compilador de “instrução inacessível”?

Muitas vezes eu acho que ao depurar um programa é conveniente (embora seja uma prática indiscutivelmente ruim) inserir uma declaração de retorno dentro de um bloco de código. Eu poderia tentar algo assim em Java ….

class Test { public static void main(String args[]) { System.out.println("hello world"); return; System.out.println("i think this line might cause a problem"); } } 

claro, isso produziria o erro do compilador.

Test.java:7: declaração inacessível

Eu pude entender por que um aviso pode ser justificado, já que ter código não utilizado é uma má prática. Mas não entendo por que isso precisa gerar um erro.

Isso é apenas Java tentando ser uma babá, ou há uma boa razão para tornar isso um erro de compilador?

Porque código inacessível é sem sentido para o compilador. Embora tornar o código significativo para as pessoas seja primordial e mais difícil do que torná-lo significativo para um compilador, o compilador é o consumidor essencial do código. Os projetistas do Java entendem que o código que não é significativo para o compilador é um erro. Sua posição é que, se você tiver algum código inacessível, você cometeu um erro que precisa ser corrigido.

Há uma pergunta semelhante aqui: Código inacessível: erro ou aviso? , no qual o autor diz: “Pessoalmente eu sinto fortemente que deveria ser um erro: se o programador escreve um trecho de código, deve sempre ser com a intenção de executá-lo em algum cenário.” Obviamente, os designers de linguagem de Java concordam.

Se um código inacessível deve impedir a compilation, é uma questão sobre a qual nunca haverá consenso. Mas é por isso que os projetistas de Java fizeram isso.


Várias pessoas nos comentários apontam que existem muitas classs de códigos inacessíveis que o Java não impede a compilation. Se eu entendi as conseqüências de Gödel corretamente, nenhum compilador pode pegar todas as classs de código inacessível.

Testes de unidade não podem capturar todos os erros. Nós não usamos isso como um argumento contra o seu valor. Da mesma forma, um compilador não pode capturar todo o código problemático, mas ainda é valioso para ele evitar a compilation de código incorreto quando puder.

Os projetistas da linguagem Java consideram um código inacessível um erro. Portanto, impedindo a compilation quando possível é razoável.


(Antes que você downvote: a questão não é ou não Java deve ter um erro de compilador de instrução inacessível. A questão é por que Java tem um erro de compilador de instrução inacessível. Não downvote-me apenas porque você acha que Java fez a decisão de design errado.)

Não há razão definitiva para que declarações inalcançáveis ​​não sejam permitidas; outras linguagens permitem sem problemas. Para sua necessidade específica, esse é o truque comum:

 if (true) return; 

Parece absurdo, quem lê o código vai adivinhar que deve ter sido feito deliberadamente, não um erro descuidado de deixar o resto das declarações inacessíveis.

Java tem um pouco de suporte para “compilation condicional”

http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21

 if (false) { x=3; } 

não resulta em erro de tempo de compilation. Um compilador de otimização pode perceber que a instrução x = 3; nunca será executado e pode optar por omitir o código dessa declaração do arquivo de class gerado, mas a instrução x = 3; não é considerado “inacessível” no sentido técnico especificado aqui.

A justificativa para esse tratamento diferente é permitir que os programadores definam “variables ​​de sinalizador”, como:

 static final boolean DEBUG = false; 

e depois escrever código como:

 if (DEBUG) { x=3; } 

A idéia é que deve ser possível alterar o valor de DEBUG de false para true ou de true para false e, em seguida, compilar o código corretamente sem outras alterações para o texto do programa.

É babá. Eu sinto que o Net tem esse direito – ele gera um aviso para código inacessível, mas não um erro. É bom ser avisado sobre isso, mas não vejo razão para impedir a compilation (especialmente durante as sessões de debugging em que é bom fazer um retorno para ignorar algum código).

Eu só notei essa questão e queria adicionar meus $ .02 a isso.

No caso do Java, isso não é realmente uma opção. O erro “código inacessível” não vem do fato de que os desenvolvedores da JVM pensavam em proteger os desenvolvedores de qualquer coisa, ou ser extremamente vigilantes, mas a partir dos requisitos da especificação da JVM.

Tanto o compilador Java quanto a JVM usam o que é chamado de “mapas de pilha” – uma informação definida sobre todos os itens na pilha, conforme alocados para o método atual. O tipo de cada slot da pilha deve ser conhecido, para que uma instrução JVM não trate mal o item de um tipo para outro tipo. Isso é importante principalmente para evitar que um valor numérico seja usado como um ponteiro. É possível, usando o assembly Java, tentar empurrar / armazenar um número, mas então pop / load uma referência de object. No entanto, a JVM rejeitará esse código durante a validação de class – isto é, quando os mapas de pilha estiverem sendo criados e testados quanto à consistência.

Para verificar os mapas de pilha, a VM precisa percorrer todos os caminhos de código existentes em um método e certificar-se de que não importa qual caminho de código será executado, os dados da pilha para cada instrução estão de acordo com o que qualquer código anterior / armazenado na pilha. Então, no caso simples de:

 Object a; if (something) { a = new Object(); } else { a = new String(); } System.out.println(a); 

na linha 3, a JVM verificará se os dois ramos de ‘if’ foram armazenados apenas em um (que é apenas local var # 0) algo que seja compatível com Object (já que o código da linha 3 e on tratará o var local # 0 ).

Quando o compilador chega a um código inacessível, ele não sabe exatamente qual estado a pilha pode estar nesse ponto, portanto, não pode verificar seu estado. Ele não consegue compilar mais o código nesse ponto, já que ele não pode acompanhar variables ​​locais também, então, em vez de deixar essa ambigüidade no arquivo de class, ele produz um erro fatal.

É claro que uma condição simples como if (1<2) irá enganá-lo, mas não é realmente enganador - está dando a ele um branch potencial que pode levar ao código, e pelo menos tanto o compilador quanto a VM podem determinar, como a pilha itens podem ser usados ​​a partir daí.

PS Eu não sei o que o .NET faz neste caso, mas acredito que ele irá falhar na compilation também. Isso normalmente não será um problema para qualquer compilador de código de máquina (C, C ++, Obj-C, etc.)

Um dos objectives dos compiladores é excluir classs de erros. Algum código inacessível está lá por acidente, é legal que o javac exclua essa class de erro em tempo de compilation.

Para cada regra que pega código errôneo, alguém vai querer que o compilador aceite porque sabe o que está fazendo. Essa é a penalidade da verificação do compilador, e obter o equilíbrio certo é um dos pontos mais complicados do design da linguagem. Mesmo com a verificação mais rigorosa ainda existe um número infinito de programas que podem ser escritos, então as coisas não podem ser tão ruins assim.

Embora eu ache que esse erro do compilador é bom, existe uma maneira de contornar isso. Use uma condição que você sabe que será verdadeira:

 public void myMethod(){ someCodeHere(); if(1 < 2) return; // compiler isn't smart enough to complain about this moreCodeHere(); } 

O compilador não é inteligente o suficiente para reclamar disso.

É certamente uma boa coisa reclamar que quanto mais rigoroso o compilador é melhor, na medida em que lhe permite fazer o que você precisa. Normalmente, o pequeno preço a pagar é comentar o código, o ganho é que quando você compila seu código funciona. Um exemplo geral é o de Haskell, sobre o qual as pessoas gritam até perceberem que seu teste / debugging é apenas um teste principal e um curto. Eu, pessoalmente, em Java quase não debugging enquanto sendo (na verdade de propósito) não atento.

Se a razão para permitir if (aBooleanVariable) return; someMoreCode; if (aBooleanVariable) return; someMoreCode; é permitir flags, então o fato de que if (true) return; someMoreCode; if (true) return; someMoreCode; não gera um erro de tempo de compilation parece inconsistência na política de geração de exceção CodeNotReachable, uma vez que o compilador ‘sabe’ que true não é um sinalizador (não uma variável).

Duas outras maneiras que podem ser interessantes, mas não se aplicam a desligar parte do código de um método, bem como if (true) return :

Agora, em vez de dizer if (true) return; talvez você queira dizer assert false e add -ea OR -ea package OR -ea className nos argumentos jvm. O bom é que isso permite alguma granularidade e requer a inclusão de um parâmetro extra na invocação do jvm, portanto, não há necessidade de definir um sinalizador DEBUG no código, mas por meio de argumento adicionado em tempo de execução, o que é útil quando o destino não é o máquina de desenvolvimento e recompilation e transferência de bytecode leva tempo.

Há também o modo System.exit(0) , mas isso pode ser um exagero, se você colocá-lo em Java em um JSP, ele terminará o servidor.

Além disso, Java é uma linguagem ‘nanny’, mas eu prefiro usar algo nativo como C / C ++ para ter mais controle.