Questão 1:
Por que o código a seguir é compilado sem ter uma declaração de retorno?
public int a() {
while(true);
}
Aviso: Se adicionar retorno depois de um tempo, recebo um Unreachable Code Error
.
Questão 2:
Por outro lado, por que o código a seguir compila,
public int a() {
while(0 == 0);
}
mesmo que o seguinte não.
public int a(int b) {
while(b == b);
}
java
syntax
while-loop
compilation
return
Willi Mentzel
fonte
fonte
Respostas:
Isso é coberto pelo JLS§8.4.7 :
Como o compilador sabe que o loop nunca terminará (
true
sempre é verdade, é claro), sabe que a função não pode "retornar normalmente" (deixar o final do corpo) e, portanto, não há problemareturn
.No
0 == 0
caso, o compilador sabe que o loop nunca terminará (isso0 == 0
sempre será verdadeiro). Mas não sabe dissob == b
.Por que não?
O compilador entende expressões constantes (§15.28) . Citação §15.2 - Formas de expressão (porque estranhamente esta frase não está em §15.28) :
No seu
b == b
exemplo, como há uma variável envolvida, ela não é uma expressão constante e não é especificada para ser determinada no momento da compilação. Podemos ver que sempre será verdade neste caso (embora, seb
fosse umdouble
, como o QBrute apontou , poderíamos ser facilmente enganadosDouble.NaN
, o que não==
é o próprio ), mas o JLS especifica apenas que expressões constantes são determinadas em tempo de compilação , ele não permite que o compilador tente avaliar expressões não constantes. O bayou.io levantou um bom argumento: por que não: Se você começa a tentar determinar expressões que envolvem variáveis no momento da compilação, por onde você pára?b == b
é óbvio (er, para nãoNaN
valores), mas e quantoa + b == b + a
? Ou(a + b) * 2 == a * 2 + b * 2
? Desenhar a linha em constantes faz sentido.Portanto, como não "determina" a expressão, o compilador não sabe que o loop nunca será encerrado; portanto, ele acha que o método pode retornar normalmente - o que não é permitido, porque é necessário usá-lo
return
. Por isso, reclama da falta de umreturn
.fonte
Pode ser interessante pensar em um tipo de retorno de método não como uma promessa de retornar um valor do tipo especificado, mas como uma promessa de não retornar um valor que não seja do tipo especificado. Portanto, se você nunca devolver nada, não estará quebrando a promessa e, portanto, qualquer um dos seguintes itens será legal:
Looping para sempre:
Recorrendo para sempre:
Lançando uma exceção:
(Acho a recursão divertida de se pensar: O compilador acredita que o método retornará um valor do tipo
X
(o que quer que seja), mas não é verdade, porque não há código presente que tenha alguma idéia de como criar ou adquirir umX
.)fonte
Observando o código de bytes, se o que está sendo retornado não corresponder à definição, você receberá um erro de compilação.
Exemplo:
for(;;)
mostrará os bytecodes:Observe a falta de qualquer bytecode de retorno
Isso nunca atinge um retorno e, portanto, não retorna o tipo errado.
Para comparação, um método como:
Retornará os seguintes bytecodes:
Observe o "retorno", que significa "retornar uma referência"
Agora, se fizermos o seguinte:
Retornará os seguintes bytecodes:
Agora podemos ver que o tipo na definição não corresponde ao tipo de retorno de retorno, o que significa return int.
Então, na verdade, o que se resume é que, se o método tiver um caminho de retorno, esse caminho deverá corresponder ao tipo de retorno. Mas há instâncias no código de bytes em que nenhum caminho de retorno é gerado e, portanto, nenhuma quebra da regra.
fonte