Eu encontrei uma situação em que um método não nulo está faltando uma instrução de retorno e o código ainda é compilado. Eu sei que as instruções após o loop while são inacessíveis (código morto) e nunca seriam executadas. Mas por que o compilador nem avisa sobre o retorno de algo? Ou por que uma linguagem nos permitiria ter um método não vazio com um loop infinito e não retornando nada?
public int doNotReturnAnything() {
while(true) {
//do something
}
//no return statement
}
Se eu adicionar uma instrução break (mesmo que condicional) no loop while, o compilador reclamará dos erros infames: Method does not return a value
no Eclipse e Not all code paths return a value
no Visual Studio.
public int doNotReturnAnything() {
while(true) {
if(mustReturn) break;
//do something
}
//no return statement
}
Isso vale para Java e C #.
unreachable statement
se houver algo após o loop.Respostas:
A regra para métodos não nulos é todo caminho de código retornado deve retornar um valor , e essa regra é satisfeita no seu programa: zero dos zero caminhos de código retornados retornam um valor. A regra não é "todo método não nulo deve ter um caminho de código que retorne".
Isso permite que você escreva métodos stub como:
Esse é um método não vazio. Ele tem de ser um método não-nula, a fim de satisfazer a interface. Mas parece bobagem tornar essa implementação ilegal porque ela não retorna nada.
O fato de seu método ter um ponto final inacessível por causa de um
goto
(lembre-se, awhile(true)
é apenas uma maneira mais agradável de escrevergoto
) em vez de umthrow
(que é outra forma degoto
) não é relevante.Porque o compilador não tem boas evidências de que o código esteja errado. Alguém escreveu
while(true)
e parece provável que a pessoa que fez isso sabia o que estava fazendo.Veja meus artigos sobre o assunto, aqui:
ATBG: acessibilidade de fato e de jure
E você também pode considerar ler a especificação C #.
fonte
public int doNotReturnAnything() {
boolean flag = true;
while (flag) {
//Do something
}
//no return
}
esse código também não possui um ponto de interrupção. Então agora como o compilador sabe que o código está errado.if
,while
,for
,switch
, etc, as construções de ramificação que operam sobre as constantes são tratados como desvios incondicionais pelo compilador. A definição exata de expressão constante está na especificação. As respostas para suas perguntas estão na especificação; meu conselho é que você leia.Every code path that returns must return a value
Melhor resposta. Aborda claramente as duas questões. ObrigadoO compilador Java é inteligente o suficiente para encontrar o código inacessível (o código após o
while
loop)e, como é inacessível , não faz sentido adicionar uma
return
declaração lá (após owhile
término)o mesmo acontece com condicional
if
como a condição booleana
someBoolean
só pode ser avaliada como umatrue
ou outrafalse
, não há necessidade de fornecer uma apósreturn
explicitamenteif-else
, porque esse código é inacessível e o Java não se queixa.fonte
return
declaração no código inacessível, mas não tem nada a ver com o motivo pelo qual você não precisa de nenhumareturn
declaração no código do OP.public int doNotReturnAnything() {
if(true){
System.exit(1);
}
return 11;
}
public int doNotReturnAnything() {
while(true){
System.exit(1);
}
return 11;// Compiler error: unreachable code
}
System.exit(1)
irá matar o programa. Ele só pode detectar certos tipos de código inacessível.System.exit(1)
que matará o programa, podemos usar qualquer umreturn statement
, agora o compilador reconhece oreturn statements
. E o comportamento é o mesmo, o retorno requer,if condition
mas não parawhile
.while(true)
O compilador sabe que o
while
loop nunca irá parar de executar; portanto, o método nunca será concluído; portanto, umareturn
instrução não é necessária.fonte
Dado que seu loop está sendo executado em uma constante - o compilador sabe que é um loop infinito - o que significa que o método nunca poderia retornar.
Se você usar uma variável - o compilador aplicará a regra:
Isso não será compilado:
fonte
A especificação Java define um conceito chamado
Unreachable statements
. Você não tem permissão para ter uma instrução inacessível no seu código (é um erro de tempo de compilação). Você não tem permissão para ter uma declaração de retorno depois de um tempo (true); declaração em Java. Umawhile(true);
instrução torna as seguintes instruções inacessíveis por definição; portanto, você não precisa de umreturn
instrução.Observe que, embora o problema de parada seja indecidível em casos genéricos, a definição de Instrução inacessível é mais rigorosa do que apenas parar. Está decidindo casos muito específicos em que um programa definitivamente não é interrompido. Teoricamente, o compilador não é capaz de detectar todos os loops infinitos e instruções inacessíveis, mas precisa detectar casos específicos definidos na especificação (por exemplo, o
while(true)
caso)fonte
O compilador é inteligente o suficiente para descobrir que seu
while
loop é infinito.Portanto, o compilador não pode pensar em você. Não dá para adivinhar por que você escreveu esse código. O mesmo vale para os valores de retorno dos métodos. Java não vai reclamar se você não fizer nada com os valores de retorno do método.
Então, para responder sua pergunta:
O compilador analisa seu código e, depois de descobrir que nenhum caminho de execução leva a cair no final da função, ele termina com OK.
Pode haver razões legítimas para um loop infinito. Por exemplo, muitos aplicativos usam um loop principal infinito. Outro exemplo é um servidor web que pode esperar indefinidamente por solicitações.
fonte
Na teoria dos tipos, existe algo chamado tipo inferior que é uma subclasse de qualquer outro tipo (!) E é usado para indicar não terminação, entre outras coisas. (Exceções podem contar como um tipo de não-rescisão - você não termina pelo caminho normal.)
Portanto, de uma perspectiva teórica, pode-se considerar que essas declarações que não terminam retornam algo do tipo Bottom, que é um subtipo de int, para que você (tipo de) obtenha seu valor de retorno, afinal, de uma perspectiva de tipo. E é perfeitamente aceitável que não faça sentido que um tipo possa ser uma subclasse de todo o resto, incluindo int, porque você nunca retorna um.
De qualquer forma, por meio da teoria explícita dos tipos ou não, os compiladores (escritores do compilador) reconhecem que solicitar um valor de retorno após uma declaração não-terminante é tolo: não há um caso possível em que você possa precisar desse valor. (Pode ser bom ter o seu compilador avisando quando algo não terminar, mas parece que você deseja retornar algo. Mas isso é melhor para os verificadores de estilo a la lint, pois talvez você precise da assinatura de tipo que por algum outro motivo (por exemplo, subclassificação), mas você realmente deseja não rescisão.)
fonte
Não há situação em que a função possa chegar ao fim sem retornar um valor apropriado. Portanto, não há nada para o compilador reclamar.
fonte
O Visual Studio possui o mecanismo inteligente para detectar se você digitou um tipo de retorno, e deve ter uma instrução de retorno na função / método
Como no PHP Seu tipo de retorno é verdadeiro se você não retornou nada. compilador obter 1 se nada tiver retornado.
A partir deste
O compilador sabe que a declaração while possui uma natureza infinita, portanto, não deve ser considerada. O compilador php será automaticamente verdadeiro se você escrever uma condição na expressão while.
Mas não no caso do VS, ele retornará um erro na pilha.
fonte
Seu loop while funcionará para sempre e, portanto, não sairá enquanto; continuará sendo executado. Portanto, a parte externa de while {} está inacessível e não faz sentido escrever ou não retornar. O compilador é inteligente o suficiente para descobrir qual parte é alcançável e qual não é.
Exemplo:
O código acima não será compilado, porque pode haver um caso em que o valor da variável x seja modificado dentro do corpo do loop while. Portanto, isso torna a parte externa do loop while acessível! E, portanto, o compilador lançará um erro 'nenhuma declaração de retorno encontrada'.
O compilador não é inteligente o suficiente (ou melhor, preguiçoso;)) para descobrir se o valor de x será modificado ou não. Espero que isso limpe tudo.
fonte
int x = 1; while(x * 0 == 0) { ... }
era um loop infinito, mas a especificação diz que o compilador deve fazer deduções de fluxo de controle apenas quando a expressão do loop é constante , e a especificação define uma expressão constante como não contendo variáveis . O compilador era, portanto, muito inteligente . No C # 3, fiz o compilador corresponder à especificação e, desde então, é deliberadamente menos inteligente sobre esse tipo de expressão."Por que o compilador nem avisa sobre o retorno de algo? Ou por que uma linguagem nos permite ter um método não vazio com um loop infinito e sem retornar nada?".
Este código também é válido em todos os outros idiomas (provavelmente exceto Haskell!). Como a primeira suposição é que estamos "intencionalmente" escrevendo algum código.
E há situações em que esse código pode ser totalmente válido, como se você fosse usá-lo como um encadeamento; ou se estava retornando a
Task<int>
, você poderia fazer alguma verificação de erro com base no valor int retornado - que não deve ser retornado.fonte
Posso estar errado, mas alguns depuradores permitem a modificação de variáveis. Aqui, enquanto x não é modificado pelo código e será otimizado pelo JIT, pode-se modificar x para false e o método deve retornar algo (se isso for permitido pelo depurador de C #).
fonte
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 .
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 :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 qualquerwhile
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:
Com:
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):
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.fonte