Java의 finally 블록에서 반환


Answers

당신이 제공 한 예제는 결국 흐름 제어를 사용 하지 않을 만큼 충분한 이유입니다.

"더 나은"인위적인 예제가 있다고하더라도 나중에 코드를 유지 관리해야하는 개발자와 미묘한 부분을 인식하지 못하는 개발자를 고려하십시오. 그 불쌍한 개발자가 너일지도 몰라 ....

Question

최근 Java에서 finally 블록에 return 문을 사용할 수 있다는 사실을 알게 된 것에 놀랐습니다.

많은 사람들이 ' 마침내 돌아 오지 마라 '에서 설명한대로 할 일이 좋지 않다고 생각하는 것 같습니다. 조금 더 긁어서, 나는 또한 자바의 리턴이 항상 그렇지는 않다는 것을 발견했다. 이것은 결국 블럭에서 다른 유형의 플로우 컨트롤에 대한 아주 끔찍한 예제를 보여준다.

그래서, 내 질문은, 나에게 반환 문장 (또는 다른 흐름 제어)에서 마침내 블록 더 나은 / 더 읽을 수있는 코드를 생성하는 예제를 줄 수 있습니까?




간단한 Groovy 테스트 :

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)

산출:

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 ^^
-----------------------------------------------------------------------------

문제:

흥미로운 점 중 하나는 Groovy가 암시 적 리턴을 처리하는 방법을 확인하는 것이 었습니다. Groovy에서는 메서드를 "반환"하는 것만으로 끝 부분에 값을 남겨 둘 수 있습니다 (반환하지 않음). finally 문에서 runningThreads.remove (..) 행의 주석 처리를 제거하면 일반 반환 값 ( "OK")을 덮어 쓰고 예외를 덮을 것입니까?




나는 이로 인해 야기 된 버그를 수년 전에 추적하기가 정말로 어려웠습니다. 코드는 다음과 같습니다.

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

예외는 다른 코드에서 발생했습니다. 그것은 잡히고 기록되고 somethingThatThrewAnException() 메서드 내에서 다시 throw됩니다. 그러나 예외는 problemMethod() 지나쳐 전파되지 않았습니다. 이것을 보는 데 오랜 시간이 걸린 후에 마침내 그것을 반환 방법으로 추적했습니다. finally 블록의 return 메서드는 기본적으로 try 블록에서 발생한 예외가 catch되지 않은 경우에도 전파되는 것을 막습니다.

다른 사람들처럼 Java 스펙에 따라 finally 블록에서 돌아 오는 것은 합법적이지만 잘못된 결과 일 수는 없다고 말했습니다.




마지막으로 {} 블록에 제어 구조와 리턴을 추가하는 것은 거의 모든 개발 언어에 흩어져있는 악용을 "할 수 있기 때문에"의 또 다른 예입니다. 제이슨은 쉽게 유지 보수의 악몽이 될 수 있다고 제안했다. 함수의 조기 반환에 대한 논쟁은 "늦게 돌아 오는"사례에 더 많이 적용된다.

마지막으로 블록은 하나의 목적을 위해 존재하며 모든 이전 코드에서 어떤 일이 발생했는지에 상관없이 자신을 완전히 정리할 수 있습니다. 기본적으로 이것은 파일 포인터, 데이터베이스 연결 등을 닫거나 놓는 것입니다.하지만 맞춤식 감사 기능을 추가한다고 말하는 것을 볼 수는 있습니다.

함수 반환에 영향을주는 것은 try {} 블록에 있어야합니다. 외부 상태를 검사하고 시간이 많이 소요되는 작업을 수행 한 다음 유효하지 않은 경우 다시 상태를 확인하는 방법이 있더라도 try {} 내부에서 두 번째 검사가 필요합니다. 그리고 장시간 작업이 실패하면, 두 번째로 불필요하게 그 상태를 점검 할 것입니다.




javac은 -Xlint : finally를 사용하면 마침내 리턴 값을 경고합니다. 원래 javac는 경고를 표시하지 않았습니다. 코드에 문제가 있으면 컴파일에 실패합니다. 불행히도 하위 호환은 예기치 않은 독창적 인 어리 석음이 금지 될 수 없음을 의미합니다.

finally 블록에서 예외가 발생할 수 있지만이 경우 전시 된 동작은 거의 확실하게 원하는 것입니다.