Programming/Java

[Youtube][이팩티브 자바] #9 Try-with-Resource

bisi 2020. 5. 26. 10:38

백기선님의 유투브 강의 내용을 정리하였습니다.

 

백기선님 Github 바로가기

Youtube 바로가기

 

 

강의내용 필기 


주제 9 : Try-Finally 대신 Try-with-Resource 사용하라

 

자바 라이브러리에서는 InputStream, OutputStream 그리고 java,sql.Connection과 같이 정리(close)가 가능한 리소스가 많은데, 그런 리소스를 사용하는 클라이언트 코드가 보통 리소스 정리를 안하거나 잘못하는 경우가 있다. 

 

public class FirstError extends RuntimeException {
}
public class SecondException extends RuntimeException {
}
public class MyResource implements AutoCloseable {

    public void doSomething() throws FirstError {
        System.out.println("doing something");
        throw new FirstError();
    }

    @Override
    public void close() throws SecondException {
        System.out.println("clean my resource");
        throw new SecondException();
    }
}

위의 코드를 가지고 실행하는 클래스를 만든다. 

 

아래와 같은 코드는 doSomething메소드에서 Exception이 발생하면 close()메소드까지 못간다.

public class MyRunner {
    public static void main(String[] args) {
        // 이렇게 하면 안된다. dosomething에서 Exception이 발생하면 close를 못하기 때문에
        MyResource myResource = new MyResource();
        myResource.doSomething();
        myResource.close();
    }
}

 

고전적인 방법으로 try-finally를 해보자.

문제는 코드가 장황해질수 있다. 

MyResource myResource = null;
try {
    myResource = new MyResource();
    myResource.doSomething();
} finally {
    if (myResource != null) {
        myResource.close();
    }
}

 

또한 아래와 같은 nested구조일 때, 구현하기가 쉽지 않다. 

public class MyRunner {
    public static void main(String[] args) {

        MyResource myResource = null;
        try{
            myResource = new MyResource();
            myResource.doSomething();
            MyResource secondResource = null;
            try{
                secondResource = new MyResource();
                secondResource.doSomething();
            }finally {
                if (secondResource != null)
                    secondResource.close();
            }            
        }finally {
            if (myResource != null)
            myResource.close();
        }
    }

}

 

이 코드에서 예외가 발생하면 SecondException이 출력되고 FirstException은 덮힌다. 즉 FirstException은 안 보여서 확인할 수가 없다. 그러면 문제를 디버깅하기 힘들어 진다. 또한 중복으로 try-catch를 만들어야 하는 경우에도 실수를 할 가능성이 높다.

 

자바7에 추가된 Try-with-Resource를 사용하면 코드 가독성도 좋고, 문제를 분석할 때도 훨씬 좋다. 왜냐하면 Try-Finally를 사용할 때 처럼 처음에 발생한 예외가 뒤에 발생한 에러가 덮히지 않기 때문이다.

 

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

 close를 명시적으로 사용하지 않아도 Autocloseable을 사용하면 자동으로 호출해준다.

 

뒤에 발생한 에러는 첫번째 발생한 에러 뒤에다 쌓아두고 (suppressed) 처음 발생한 에러를 중요시 여긴다. 그리고 Throwable의 getSuppressed 메소드를 사용해서 뒤에 쌓여있는 에러를 코딩으로 사용할 수도 있다. 

catch 블록은 Try-Finally와 동일하게 사용할 수 있다. 

 

또, 아래처럼 2개로 적용해서 사용할 수 있다. 

try(MyResource myResource = new MyResource(); MyResource secondResource = new MyResource()){
        myResource.doSomething();
        secondResource.doSomething();
    }

이것도 close() 2개 다 실행된다.