ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 객체의 생성과 파괴 - 아이템 9. try-finally보다는 try-with-resource를 사용하라
    Study/Effective Java 2022. 12. 13. 14:07

    try-finally보다는 try-with-resource를 사용하라

    try-finally 문제점

    자바 라이브러리에는 close() 메서드를 호출해 직접 닫아줘야 하는 자원이 많다. InputStream, OutputStream, java.sql.Connection 등이 그 예이다. 자원 닫기는 클라이언트가 놓치기 쉬워서 예측할 수 없는 문제로 이어지기도 한다.

     

    static String firstLineOfFile(String path) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(path));
        try {
            return br.readLine();
        } finally {
            br.close();
        }
    }

     

    위 코드는 BufferedReader를 사용한 뒤 close() 메서드를 호출해 직접 닫아주는 코드이다. 전통적으로 자원이 제대로 닫힘을 보장하는 수단인 try-finally 블록을 사용하였다.

    try-finally 블록은 try 블록이 끝난 뒤 실행할 로직을 정의해 주는 블록이다. 따라서, 이제 IOException이 발생하게 되더라도 상위 메서드로 IOException 객체를 던져준 뒤 finally 블록 내 메서드를 호출한 후 종료하게 된다.

     

    만약, close() 메서드를 여러 번 호출해야 하는 상황이 오면 어떻게 해야 할까? 아래에서 살펴보겠다.

     

    static void copy(String src, String dst) throws IOException {
        InputStream in = new FileInputStream(src);
        try {
            OutputStream out = new FileOutputStream(dst);
            try {
                byte[] buf = new byte[BUFFER_SIZE];
                int n;
                while ((n = in.read(buf)) >= 0)
                    out.write(buf, 0, n);
            } finally {
                out.close();
            }
        } finally {
            in.close();
        }
    }

    위 소스 코드는 자원에 대해 close() 메서드를 두 번 사용하는 코드이다. close() 메서드가 두 번 사용함에 따라 try-finally 블록이 중복돼서 사용하는 것을 확인할 수 있다. 이처럼, 사용 자원이 둘 이상일 경우 소스코드가 복잡해지고 더러워진다는 것을 알 수 있다.

     

    try-finally 문을 제대로 사용한 앞의 두 코드 예제에서는 결점이 한 가지 있다. 예외는 try 블록과 finally 블록 모두에서 발생할 수 있다. 예를 들어, 기기에 물리적인 문제가 생긴다면 firstLineOfFile() 메서드 안의 readLine() 메서드가 예외를 던지고, 같은 이유로 close() 메서드 또한 실패할 것이다.

    위와 같은 상황이라면 구 번째 예외가 첫 번째 예외를 완전히 집어삼켜 버린다. 그 결과, 스택 추적 내역에 첫 번째 예외에 관한 정보는 남지 않게 되어, 실제 시스템에서의 디버깅을 몹시 어렵게 할 것이다.

     

    try-with-resources의 사용

    위에서 언급한 try-finally 방식의 단점을 보완하기 위해 Java 7부터는 try-with-resources가 도입되었다. try-with-resources문을 사용하기 위해서는 사용하는 자원이 AutoCloseable 인터페이스를 구현해야 한다.

     

    public interface AutoCloseable {
        void close() throws Exception;
    }

    AutoCloseable 인터페이스는 close() 메서드 하나만을 정의해 놓은 간단한 인터페이스이며, 자바 라이브러리와 서드파티 라이브러리의 수많은 클래스 및 인터페이스에서는 이미 AutoCloseable 인터페이스를 구현하거나 확장해 두었다.

     

    따라서, close() 메서드가 필요한 자원 클래스를 커스텀할 일이 있다면, AutoCloseable 인터페이스를 반드시 구현할 것을 권장한다.

     

    static String firstLineOfFile(String path) throws IOException {
        try (BufferedReader br = new BufferedReader(new FileReader(path))) {
            return br.readLine();
        }
    }

    try-with-resources 블록을 사용해서 firstLineOfFile() 메서드를 재구성해 보았다.

     

    static void copy(String src, String dst) throws IOException {
        try (InputStream in = new FileInputStream(src);
            OutputStream out = new FileOutputStream(dst)) {
            byte[] buf = new byte[BUFFER_SIZE];
            int n;
            while ((n = in.read(buf)) >= 0)
                out.write(buf, 0, n);
        }
    }

    마찬가지로, try-with-resources 블록을 사용해서 copy() 메서드를 재구성해 보았다.

     

    try-with-resources 블록을 사용하여 재구성한 소스코드가 짧고 읽기 수월할 뿐 아니라 문제를 진단하기에도 훨씬 좋다. 재구성하기 전 firstLineOfFile() 메서드를 생각해보면 readLine()과 close() 메서드 양쪽에서 예외가 발생하면, readLine() 메서드에서 발생한 예외는 숨겨지고 close() 메서드에서 발생한 예외가 기록된다. 하지만, try-with-resources 블록을 사용하면 숨겨진 예외들도 그냥 버려지지 않고, 스택 추적 내역에 'suppressed' 꼬리표를 달고 출력된다.

     

    static String firstLineOfFile(String path) throws IOException {
        try (BufferedReader br = new BufferedReader(new FileReader(path))) {
            return br.readLine();
        } catch (IOException e) {
            return defaultVal;
        }
    }

    또한, try-with-resources 블록에서도 위와 같이 catch 블록을 사용할 수 있다. catch 블록 덕분에 try 문을 중첩하지 않고도 다수의 예외를 처리할 수 있다는 장점이 있다.

     

    최종 정리

    꼭 회수해야 하는 자원을 다룰 때는 try-finally 말고, try-with-resources를 사용하자. 예외는 없다. 코드는 더 짧고 분명해지고, 만들어지는 예외 정보도 훨씬 유용하다. try-finally로 작성하면 실용적이지 못할 만큼 코드가 지저분해지는 경우라도, try-with-resources로는 정확하고 쉽게 자원을 회수할 수 있다.


    출처

     이펙티브 자바 Effective Java 3/E. 조슈아 블로크 저자(글) · 개앞맵시(이복연) 번역

    https://github.com/woowacourse-study/2022-effective-java/blob/main/02%EC%9E%A5/%EC%95%84%EC%9D%B4%ED%85%9C_09/try-finally%EB%B3%B4%EB%8B%A4%EB%8A%94_try-with-resources%EB%A5%BC_%EC%82%AC%EC%9A%A9%ED%95%98%EB%9D%BC.md

     

    728x90

    댓글

Designed by Tistory.