try-with-resources에 의해 닫히지 않는 AutoCloseable 인스턴스

주싱·2023년 4월 13일
1

더 나은 도구

목록 보기
8/13

Closed icons created by Smashicons - Flaticon

아래와 같은 코드에서 try 블럭 내에서 생성되어 BufferedInputStream에게 전달되는 FileInputStream 인스턴스는 try-with-resources 문에 의해 자동으로 close 되는건지 의문이 생겼다. 이 블로그 글을 쓰신 분에게 도움을 많이 받았다. 결론은 try 블럭 내에서 명시적인 변수로 선언되지 않은 인스턴스는 자동으로 close 되지 않는다.

try (InputStream inputStream = new BufferedInputStream(new FileInputStream(srcFile))) {
    dstBuffer.writeBytes(inputStream, (int) srcFile.length());
} ... 

그러나 BufferedInputStream 구현을 살펴보면 close할 때 내부 참조 스트림을 함께 닫아주는 것을 볼 수 있다. 위와 같은 상황에서 결론적으로 try-with-resources 문에 의해 닫기가 보장되는 것이 아니라 BufferedInputStream 구현에 의해 닫기가 보장되는 것임을 알 수 있다. try-with-resources 문을 사용할 때 주의가 필요하겠다.

public void close() throws IOException {
    byte[] buffer;
    while ( (buffer = buf) != null) {
        if (U.compareAndSetReference(this, BUF_OFFSET, buffer, null)) {
            InputStream input = in;
            in = null;
            if (input != null)
                input.close(); // 내부 참조 스트림을 닫습니다.
            return;
        }
        // Else retry in case a new buf was CASed in fill()
    }
}

최종적으로 이해한 것을 테스트 코드로 작성해 보면 다음과 같다.

public class TryWithResourcesTest {

    @Test
    void notDeclaredAsVariable() throws Exception {
        try (Outer outer = new Outer(new Inner())) {
            // do something
        }
        // Result :
        // Outer.close()
    }

    @Test
    void declaredAsVariable() throws Exception {
        try (Inner inner = new Inner();
             Outer outer = new Outer(inner)) {
            // do something
        }
        // Result : 
        // Outer.close()
        // Inner.close()
    }

    static class Outer implements AutoCloseable {
        Inner inner;

        Outer(Inner inner) {
            this.inner = inner;
        }

        @Override
        public void close() throws Exception {
            System.out.println("Outer.close()");
        }
    }

    static class Inner implements AutoCloseable {
        @Override
        public void close() throws Exception {
            System.out.println("Inner.close()");
        }
    }
}
profile
소프트웨어 엔지니어, 일상

0개의 댓글