Java에서 finally 블록에서 복귀


177

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

많은 사람들이 ' finally 절에 반환하지 마십시오 '에 설명 된 것처럼 나쁜 일이라고 생각하는 것 같습니다 . 좀 더 깊게 긁으면 서 ' 자바의 리턴은 항상 그렇지는 않다 '는 것을 발견했습니다. 이것은 마침내 블록에서 다른 유형의 흐름 제어의 끔찍한 예를 보여줍니다.

그래서 내 질문은, 누군가가 finally 블록의 return 문 (또는 다른 흐름 제어)이 더 나은 / 더 읽기 쉬운 코드를 생성하는 예를 줄 수 있습니까?

답변:


90

귀하가 제공 한 예 는 최종적으로 흐름 제어를 사용 하지 않을 수있는 충분한 이유 입니다.

"더 나은"사례가 있다고하더라도 나중에 코드를 관리해야하고 미묘한 부분을 모르는 개발자를 고려하십시오. 그 가난한 개발자는 당신일지도 모른다 ....


5
확실한. 누군가가 나에게 선의 측면에서 정말로 매력적인 예를 줄 수있는 경우를 묻는 것 같아요.
매트 셰퍼드

daos의 @MattSheppard, 나는 종종 시도
Blake

148

몇 년 전 이로 인한 버그를 추적하기가 정말 힘들었습니다. 코드는 다음과 같습니다.

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

다른 코드에서 예외가 발생했습니다. somethingThatThrewAnException()메소드 내에서 포착 및 기록되어 다시 던져 졌습니다. 그러나 예외는 과거에 전파되지 않았습니다 problemMethod(). 이것을 오랫동안 본 후 우리는 마침내 그것을 return 메소드로 추적했습니다. finally 블록의 return 메소드는 기본적으로 try 블록에서 발생한 예외가 포착되지 않더라도 전파되는 것을 막고있었습니다.

다른 사람들이 말했듯이 Java 사양에 따라 finally 블록에서 반환하는 것이 합법적이지만 나쁜 일이므로 수행해서는 안됩니다.


그렇다면 어디로 반품해야합니까?
파 섹터

@parsecer try 블록 내에서 somethingThatThrewAnException ()을 호출 한 직후에 말하고 싶습니다
Tiago Sippert

@parsecer, ?? 마지막으로 평소대로하십시오.
Pacerier

21

-Xlint : finally를 사용하면 javac는 마지막으로 리턴을 경고합니다. 원래 javac는 경고를 표시하지 않았습니다. 코드에 문제가 있으면 컴파일에 실패해야합니다. 불행하게도 이전 버전과의 호환성은 예기치 않은 독창적 인 어리 석음을 막을 수 없음을 의미합니다.

finally 블록에서 예외가 발생할 수 있지만이 경우 표시되는 동작은 거의 확실합니다.


13

제어 구조와 finally {} 블록에 리턴을 추가하는 것은 사실상 모든 개발 언어에 흩어져있는 "할 수 있기 때문에"남용의 또 다른 예일뿐입니다. Jason은 쉽게 유지 보수의 악몽이 될 수 있다고 제안했습니다. 함수의 초기 리턴에 대한 논거는이 "늦은 리턴"의 경우에 더 많이 적용됩니다.

마지막으로 블록은 한 가지 목적으로 존재하므로 모든 선행 코드에서 무슨 일이 있어도 자신을 완전히 정리할 수 있습니다. 기본적으로 이것은 파일 포인터, 데이터베이스 연결 등을 닫거나 릴리스합니다. 주문 감사에 추가한다고 말하면 확장 될 수 있습니다.

함수의 리턴에 영향을주는 것은 try {} 블록에 있어야합니다. 외부 상태를 확인하고 시간이 많이 걸리는 작업을 수행 한 다음 유효하지 않은 경우 다시 해당 상태를 확인하는 방법이 있더라도 try {} 내부에서 두 번째 확인을 원할 것입니다. 긴 작업이 실패하면 불필요하게 두 번째 상태를 확인하게됩니다.


6

간단한 그루비 테스트 :

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")을 덮어 쓰고 예외를 처리 합니까 ?!


0

finally블록 내부에서 돌아 오면 exceptions손실됩니다.

finally 블록 내부의 return 문은 try 또는 catch 블록에서 발생할 수있는 모든 예외를 버립니다.

에 따르면 Java 언어 사양 :

다른 이유로 R 시도로 try 블록의 실행이 갑자기 완료되면 finally 블록이 실행 된 다음 선택 사항이 있습니다.

   If the finally block completes normally, then the try statement
   completes  abruptly for reason R.

   If the finally block completes abruptly for reason S, then the try
   statement  completes abruptly for reason S (and reason R is
   discarded).

참고 : JLS 14.17에 따라 리턴 명령문이 항상 갑자기 완료됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.