Pergunta

Fiquei surpreso recentemente ao descobrir que é possível ter uma instrução return em um bloco final em Java.

Parece que muitas pessoas acham que é uma coisa ruim fazer o que está descrito em 'Não retorne em uma cláusula final'.Coçando um pouco mais fundo, também descobri 'O retorno do Java nem sempre' que mostra alguns exemplos horríveis de outros tipos de controle de fluxo em blocos finalmente.

Então, minha pergunta é: alguém pode me dar um exemplo em que uma instrução de retorno (ou outro controle de fluxo) em um bloco final produz um código melhor/mais legível?

Foi útil?

Solução

Os exemplos que você forneceu são razão suficiente para não use o controle de fluxo de finalmente.

Mesmo que haja um exemplo inventado onde é "melhor", considere o desenvolvedor que terá que manter seu código posteriormente e que pode não estar ciente das sutilezas.Esse pobre desenvolvedor pode até ser você....

Outras dicas

Tive MUITO dificuldade em rastrear um bug anos atrás que foi causado por isso.O código era algo como:

Object problemMethod() {
    Object rtn = null;
    try {
        rtn = somethingThatThrewAnException();
    }
    finally {
        doSomeCleanup();
        return rtn;
    }
}

O que aconteceu é que a exceção foi lançada em algum outro código.Ele estava sendo capturado, registrado e relançado dentro do somethingThatThrewAnException() método.Mas a exceção não estava sendo propagada problemMethod().Depois de muito tempo analisando isso, finalmente localizamos o método de retorno.O método de retorno no bloco final estava basicamente impedindo a propagação da exceção que aconteceu no bloco try, mesmo que não tenha sido detectada.

Como outros já disseram, embora seja legal retornar de um bloco final de acordo com as especificações Java, isso é uma coisa RUIM e não deve ser feito.

javac avisará sobre o retorno finalmente se você usar -Xlint:finalmente.Originalmente, o javac não emitiu nenhum aviso - se algo estiver errado com o código, ele deverá falhar na compilação.Infelizmente, a compatibilidade com versões anteriores significa que tolices engenhosas imprevistas não podem ser proibidas.

Exceções podem ser lançadas a partir de blocos finalmente, mas nesse caso o comportamento exibido é quase certamente o que você deseja.

Adicionar estruturas de controle e retornos a blocos finalmente{} é apenas outro exemplo de abusos do tipo "só porque você pode", que estão espalhados por praticamente todas as linguagens de desenvolvimento.Jason estava certo ao sugerir que isso poderia facilmente se tornar um pesadelo de manutenção - os argumentos contra retornos antecipados de funções se aplicam mais a este caso de "retornos tardios".

Finalmente, os blocos existem com um propósito: permitir que você se organize completamente, não importa o que tenha acontecido em todo o código anterior.Principalmente, isso é fechar/liberar ponteiros de arquivo, conexões de banco de dados, etc., embora eu possa ver isso sendo esticado para dizer a adição de auditoria personalizada.

Qualquer coisa que afete o retorno da função deve estar no bloco try{}.Mesmo se você tivesse um método pelo qual verificasse um estado externo, executasse uma operação demorada e, em seguida, verificasse esse estado novamente caso ele se tornasse inválido, você ainda desejaria a segunda verificação dentro do try{} - se ele estivesse finalmente dentro{} e a longa operação falhasse, você verificaria esse estado uma segunda vez desnecessariamente.

Um teste simples e bacana:

public class Instance {

  List<String> runningThreads = new ArrayList<String>()

  void test(boolean returnInFinally) {

    println "\ntest(returnInFinally: $returnInFinally)"
    println "--------------------------------------------------------------------------"
    println "before execute"
    String result = execute(returnInFinally, false)
    println "after execute -> result: " + result
    println "--------------------------------------------------------------------------"

    println "before execute"
    try {
      result = execute(returnInFinally, true)
      println "after execute -> result: " + result
    } catch (Exception ex) {
      println "execute threw exception: " + ex.getMessage()
    }  
    println "--------------------------------------------------------------------------\n"

  }

  String execute(boolean returnInFinally, boolean throwError) {
      String thread = Thread.currentThread().getName()
      println "...execute(returnInFinally: $returnInFinally, throwError: $throwError) - thread: $thread"
      runningThreads.add(thread)
      try {
        if (throwError) {
          println "...error in execute, throw exception"
          throw new Exception("as you liked :-)")
        }
        println "...return 'OK' from execute"
        return "OK"
      } finally {
        println "...pass finally block"
        if (returnInFinally) return "return value from FINALLY ^^"
        // runningThreads.remove(thread)
      }
  }
}

Instance instance = new Instance()
instance.test(false)
instance.test(true)

Saída:

test(returnInFinally: false)
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: false, throwError: false) - thread: Thread-116
...return 'OK' from execute
...pass finally block
after execute -> result: OK
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: false, throwError: true) - thread: Thread-116
...error in execute, throw exception
...pass finally block
execute threw exception: as you liked :-)
-----------------------------------------------------------------------------


test(returnInFinally: true)
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: true, throwError: false) - thread: Thread-116
...return 'OK' from execute
...pass finally block
after execute -> result: return value from FINALLY ^^
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: true, throwError: true) - thread: Thread-116
...error in execute, throw exception
...pass finally block
after execute -> result: return value from FINALLY ^^
-----------------------------------------------------------------------------

Pergunta:

Um ponto interessante para mim foi ver como o Groovy lida com retornos implícitos.No Groovy é possível “retornar” de um método simplesmente deixando um valor no final (sem retorno).O que você acha que acontece, se você descomentar o executandoThreads.remove(..) linha na instrução finalmente - isso substituirá o valor de retorno regular ("OK") e cobrirá a exceção?!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top