반응형

ITEM 9 "try-with-resource 를 사용하라"

 

 

이 ITEM 을 확인하기 전에 위 백기선님의 Effective Java 해설 영상을 보는 것을 추천한다.

 

자바 라이브러리에는 InputStream, OutputStream, java.sql.Connection 과 같이 정리해야 되는 리소스들이 많다.

전통적으로 try-finally 를 사용해서 많이 예외처리를 해왔는데 아래와 같은 문제점들이 있어 Java 7 부터 제공하는 try-with-resource 구문을 사용하는 것을 권장하고 있다.

 

전통적인 try-finally 문제점

 

public class MyResource implements AutoCloseable {
    public void doSomething() {
        System.out.println("Do something");
        throw new FirstError();
    }

    @Override
    public void close() throws Exception {
        System.out.println("Close my Resource");
        throw new SecondError();
    }
}

public class FirstError extends RuntimeException { }
public class SecondError extends RuntimeException { }

public class Main {
    public static void main(String[] args) {
        MyResource myResource = new MyResource();
        try {
            myResource.doSomething();
        } finally {
            myResource.close();
        }
    }
}

 

위 코드처럼 try-finally 를 사용할 경우, 첫 번째 에러는 알 수가 없다. doSomething 에서 첫 번째 에러가 발생한 후 finally 구문으로 넘어와 close 에서 두 번째 에러를 발생하는데 두 번째 에러가 첫 번째 에러를 삼켜 버린다. 보통 우리는 문제 해결을 위해 첫 번째 에러가 어디서 발생하였는지 알고 싶다.

 

public class Main {
    public static void main(String[] args) {
        MyResource myResource = new MyResource();
        try {
            myResource.doSomething();
            MyResource myResource2 = null;
            try {
                myResource2 = new MyResource();
                myResource2.doSomething();
            } finally {
                if (myResource2 != null) {
                    myResource2.close();
                }
            }
        } finally {
            myResource.close();
        }
    }
}

 

심지어 Resource 를 하나 더 생성해서 무언가를 수행하면 try-finally 구문이 nested 된 형식으로 계속해서 중첩되는 것을 볼 수 있다. 가독성도 떨어지고 실수할 가능성도 많아진다. 차라리 자바 7부터 제공하는 try-with-resource 를 사용하자.

 

public class Main {
    public static void main(String[] args) {
        try(MyResource myResource = new MyResource();
            MyResource myResource2 = new MyResource()) {
            myResource.doSomething();
            myResource2.doSomething();
        } catch (Throwable e) {
            Throwable[] suppExe = e.getSuppressed();
            System.out.println("Suppressed Exception Array" + " length = " + suppExe.length);
            for (int i = 0; i < suppExe.length; i++) {
                System.out.println("Suppressed Exceptions:");
                System.out.println(suppExe[i]);
            }
        }
    }
}
//Do something
//Close my Resource
//Close my Resource
//Suppressed Exception Array length = 2
//Suppressed Exceptions:
//org.example.SecondError
//Suppressed Exceptions:
//org.example.SecondError

 

첫 번째 에러가 발생하고 두 번째 에러가 발생했을 때 두 번째 에러를 Suppressed 로 쌓아두고 첫 번째 에러를 먼저 보여준다. 또한 catch 구문을 통해 Exception 을 받아 처리할 수도 있으며 여기서 getSuppressed API 를 통해 에러 로그들을 소스코드 단에서 직접 핸들링할 수도 있다.

 

"try-with-resources 구문은 가독성도 훌륭할 뿐 아니라 디버깅하기도 쉽다."

반응형

+ Recent posts