이펙티브 자바에 나오는 깊숙한 내용은 나중에 하기로 하고, try with resources 문에 대해서 간략하게 언급해 보도록 하겠습니다. 닫아야 할 자원. 이것들은 생각보다 어렵지 않게 찾을 수 있습니다. Stream, DB connection 등이 이에 속합니다. 저 또한 프로그램을 이런 식으로 작성하곤 했습니다.

 


 대충 A라는 친구를 새로 생성합니다. 이것을 새로 생성하면 내부적으로 리소스를 오픈하는 작업을 수행합니다. 흔히들 생각하는 인풋 스트림이나 DB connection 등이라고 생각하시면 됩니다. try 안에서 do work를 합니다. 그런데, try 내부에서 a.close를 호출하면 안 됩니다. 왜냐하면, 중간에 Exception이 걸려서, main 함수에서 catch가 되는 경우에, a.close가 호출되기도 전에, catch 절이 수행되기 때문입니다.

 

 예를 들어, A class가 다음과 같은 구조라고 해 보겠습니다. foo는 Exception을 throw 합니다. 이것은 어딘가로 '던지다' 라는 뜻을 가지는데요.

 

 

 그림으로 그리면 이런 상황입니다. 어딘가로 던져야 할 텐데.. A.foo를 호출한 쪽은 Main 함수입니다.

 

 

 정확히 말하면 이 부분입니다. 8번째 줄입니다. Exception이 던져졌습니다. 그런데 catch 문이 있습니다. 그러면, 던져진 예외를 잡을 겁니다. 그러면 Main exception 이라는 문구가 출력이 될 거에요. 이 상황을 그림으로 그려 보겠습니다.

 

 

 a.foo를 호출했을 때, Exception이 Throw가 되어서, Main에서 catch를 했다.

 

 

 그러면, 이 코드는 어떻게 동작할까요?

 

 

 a.foo는 아까와 마찬가지로 무조건 exception을 throw 합니다. 이 상황에서 ok가 print 될까요?

 

 

 아닙니다. 바로 Main exception만 출력되고 종료됩니다. ok가 print 되기 전에, 이미 Exception이 Throw가 되었고, Main에서 그것을 catch했기 때문에, 바로 Main exception으로 들어갑니다. 그래서 보통 자원을 닫는 문장을, finally 구문에서 많이 작성하곤 했습니다. 왜냐하면 catch가 잡히던, 안 잡히던 수행되었기 때문입니다.

 

 

 예를 들어, 이런 프로그램을 생각해 보겠습니다. a.foo는 항상 Exception을 던집니다.

 

 

 이 프로그램의 실행 결과는 Main exception이 뜨고 난 후에 Finally가 출력되는 것이였습니다. 그렇기 때문에, 저는 finally 안에 close를 하는 로직을 호출하곤 했습니다. 아니면, 그러한 메소드를 따로 만들어서 넣기도 했었고요.

 

 

 이런 식으로요. 그런데 보통, Stream이나 Connection의 경우에는, try catch로 또 묶어주어야 합니다. 그러면 또 finally 안에 또 try catch가 들어가 버렸습니다. 거기에다가, 자원 객체가 null이 아닌지 검사하는 null check 코드까지 작성해 주면 완벽 그 자체였습니다.

 

 코드가 지저분해 보인다고요? 사실 저렇게 중첩이 많이 쌓이는 건 좋은 건 아닙니다. 저는 3중첩 이상으로 빠지면 따로 함수를 고려하는 편입니다.

 

 


 이런 것을 간단하게 해 줄 수 있는 무언가가 없나요? try with resources는 이러한 문제를 해결해 줍니다. AutoCloseable을 구현하거나, Closeable을 구현하는 클래스가 있다고 해 보겠습니다. 아래 예제에서는 Closeable을 구현하였습니다.

 

 

 IOException을 throw 하는 close 메소드가 하나 있습니다. 이것은 문자열 "close" 를 출력합니다.

 

 

 그러면, 이런 식으로 try 소괄호 안에 A a = new A() 구문을 넣으면 됩니다. 이 정도면 정말 될까요?

 

 

 그러면, close라는 것이 출력되고, Main exception이 나옵니다. 여기서 중요한 것은 close가 출력 되었다는 점입니다. finally에 복잡하게 null check 하고 close를 하기 위해 try catch를 찍을 필요가 없다는 것이 장점이라면 장점이겠네요. 중요한 것은 코드 길이가 매우 짧아져서 읽기가 매우 쉬워졌다는 것입니다. 당연하게도, 저는 구현체에서 close가 IOException을 throw 하게 했기 때문에, Main 함수에서 IOException을 catch 해야 합니다. 그런데, Exception을 잡아도 문제가 없었습니다. 왜 그럴까요?