As especificidades do caso Java para isso (que provavelmente são muito semelhantes ao caso C #) têm a ver com a forma como o compilador Java determina se um método pode retornar.
Especificamente, as regras são que um método com um tipo de retorno não deve ser capaz de concluir normalmente e deve sempre ser concluído abruptamente (abruptamente aqui indicando por meio de uma declaração de retorno ou uma exceção) de acordo com o JLS 8.4.7 .
Se um método for declarado com um tipo de retorno, ocorrerá um erro em tempo de compilação se o corpo do método puder ser concluído normalmente. Em outras palavras, um método com um tipo de retorno deve retornar apenas usando uma instrução de retorno que forneça um retorno de valor; não é permitido "deixar cair o final do corpo" .
O compilador verifica se a terminação normal é possível com base nas regras definidas em Instruções inacessíveis do JLS 14.21 , pois também define as regras para a conclusão normal.
Notavelmente, as regras para instruções inacessíveis formam um caso especial apenas para loops que possuem uma true
expressão constante definida :
Uma instrução while pode ser concluída normalmente se pelo menos um dos seguintes for verdadeiro:
Portanto, se a while
instrução puder ser concluída normalmente , será necessária uma instrução de retorno abaixo dela, pois o código é considerado acessível e qualquer while
loop sem uma instrução de quebra acessível ou constantetrue
expressão é considerado capaz de ser concluído normalmente.
Essas regras significam que sua while
declaração com uma expressão verdadeira constante e sem a nuncabreak
é considerada concluída normalmente e, portanto, qualquer código abaixo dela nunca é considerado alcançável . O final do método está abaixo do loop, e como tudo abaixo do loop é inacessível, o mesmo ocorre com o final do método e, portanto, o método não pode ser concluído normalmente normalmente (que é o que o complier procura).
if
as instruções, por outro lado, não têm a isenção especial em relação às expressões constantes que são oferecidas aos loops.
Comparar:
// I have a compiler error!
public boolean testReturn()
{
final boolean condition = true;
if (condition) return true;
}
Com:
// I compile just fine!
public boolean testReturn()
{
final boolean condition = true;
while (condition)
{
return true;
}
}
O motivo da distinção é bastante interessante e se deve ao desejo de permitir sinalizadores de compilação condicionais que não causam erros do compilador (do JLS):
Pode-se esperar que a instrução if seja tratada da seguinte maneira:
Uma instrução if-then pode ser concluída normalmente se pelo menos uma das seguintes opções for verdadeira:
A instrução then é alcançável se a instrução if-then é alcançável e a expressão de condição não é uma expressão constante cujo valor é falso.
Uma instrução if-then-else pode ser concluída normalmente se a instrução then pode ser concluída normalmente ou a instrução else pode ser concluída normalmente.
A instrução then é alcançável se a instrução if-then-else for alcançável e a expressão da condição não for uma expressão constante cujo valor for falso.
A instrução else é alcançável se a instrução if-then-else for alcançável e a expressão da condição não for uma expressão constante cujo valor for verdadeiro.
Essa abordagem seria consistente com o tratamento de outras estruturas de controle. No entanto, para permitir que a instrução if seja usada convenientemente para fins de "compilação condicional", as regras reais diferem.
Como exemplo, a seguinte instrução resulta em um erro em tempo de compilação:
while (false) { x=3; }
porque a afirmação x=3;
não é alcançável; mas o caso superficialmente semelhante:
if (false) { x=3; }
não resulta em um erro em tempo de compilação. Um compilador otimizador pode perceber que a instrução x=3;
nunca será executada e pode optar por omitir o código dessa instrução do arquivo de classe gerado, mas a instruçãox=3;
não é considerada "inacessível" no sentido técnico especificado aqui.
A lógica desse tratamento diferente é permitir que os programadores definam "variáveis de flag", como:
static final boolean DEBUG = false;
e escreva um código como:
if (DEBUG) { x=3; }
A idéia é que seja possível alterar o valor de DEBUG de falso para verdadeiro ou de verdadeiro para falso e compilar o código corretamente, sem outras alterações no texto do programa.
Por que a instrução de interrupção condicional resulta em um erro do compilador?
Conforme citado nas regras de alcançabilidade do loop, um loop while também pode ser concluído normalmente se contiver uma instrução de interrupção acessível. Como as regras para a acessibilidade da cláusula then de uma if
instrução não levam em consideração a condição da condição , essa declaração condicional é entãoif
if
cláusula cláusula é sempre considerada alcançável.
Se break
estiver acessível, o código após o loop será novamente considerado acessível. Como não há código acessível que resulte em término abrupto após o loop, o método é considerado capaz de ser concluído normalmente e, portanto, o compilador o sinaliza como erro.