흠… klocwork가 정적 결함으로 finally 구문을 잡았다.

코드를 확인했더니 finally 구문에서 return을 호출하고 있다.
finally 사용법에 안 맞게 사용되긴 했지만 (finally는 리소스 해제 등에 활용됨)
크게 문제가 되나? 라는 생각을 가지고 테스트를 일부 해봤다.
1 2 3 4 5 6 7 8 9 |
public int whenNormal() { try { return 100; } catch (Exception e) { return 50; } finally { return 0; } } |
위는 정상적인 경우이므로 100을 리턴하길 기대한다.
하지만 finally return으로 인해 0으로 덧씌워진다.
즉, 로직과 상관없이 finally로 인해 0이 리턴된다.
1 2 3 4 5 6 7 8 9 |
public int whenThrowsException() { try { throw new Exception(); } catch (Exception e) { return 50; } finally { return 0; } } |
이 경우는 exception이 발생하여 catch절에서 50을 리턴하길 기대하지만,
위와 같은 현상으로 인해 0을 리턴하게 된다.
1 2 3 4 5 6 7 8 |
public int whenPrimitive() { int i = 100; try { return i; } finally { i = 0; } } |
만약 프리미티브 타입의 변수를 finally에서 수정하게 되면 어떻게 될까?
과연 100이 리턴될까, 0이 리턴될까?
애매할지 모르겠지만, 100이 리턴된다.
이미 리턴값을 try에서 결정한 뒤 finally 구문이 동작하기 때문이다.
1 2 3 4 5 6 7 8 9 |
public Map<String, String> whenCollections() { Map<String, String> map = Maps.newHashMap(); try { map.put("aa", "aa"); return map; } finally { map.put("bb", "bb"); } } |
그렇다면 네번째와 같이 reference 변수의 경우는 어떻게 될까?
try 구문에서 리턴값이 결정되어 있지만, 참조하고 있는 변수의 내용을 finally 구문에서 수정하기 때문에
리턴값이 가지는 reference를 찾아가면 변경된 값이 존재하므로 “aa”와 “bb”가 함께 출력된다.
호기심에 여러 가지 테스트를 해봤지만,
가장 좋은 것은 제작자의 의도(리소스 해제 등)대로 사용하는 것이다.
finally 구문은 변수를 조작하거나 return 값을 가지지 않는 것이 가장 명확할 것 같다.
@ 추가 :
실제로 테스트코드에서 문제가 있는 부분을 보게 되었는데,
jmock에서 Error를 리턴했음에도 불구하고 finally 구문을 통해 return 0; 가 되어 테스트가 성공했다.
즉, finally 구문에서 return을 정의할 경우 테스트코드의 정합성을 보장할 수 없다.
(test framework의 오류조차도 무시하므로)
추가로 알 수 있었던 것은 jmock에서 오류를 exception이 아니라 throwable()로 리턴하고 있었다.
(test case에서 catch(Exception e)를 해도 잡히지 않았음)