본문 바로가기

이팩티브 자바

[Effective] try-with-resources

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String str = in.readLine();

다음과 같이 BufferedReader를 통해 입력을 받으려고 할 때, 고려 사항

  • IOException이 발생할 수 있다.
  • close()를 통해 직접 닫아줘야 하는 자원이다.

그래서, try-finally 문법을 사용해 해당 코드를 수정한다면 다음과 같이 수정할 수 있다.

public String Input() throws IOException {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    try {
        return in.readLine();
    } finally {
        in.close();
    }
}

finally 블록은 try, catch가 끝난 뒤 실행할 로직을 정의하는 블록이기 때문에, IOException을 처리한 뒤, close() 후 종료하게 된다.

 

❗결함

💥 finally block 내의 에러 발생

public static String Input() throws IOException {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    try {
        return in.readLine();
    } finally {
        int i = 1 / 0;
        System.out.println("close check!");
        in.close();
    }
}

* close()를 실행하기 전의 finally block 내의 다른 로직에 의해 에러가 발생하면, close()가 실행되지 않을 수 있다.

 

💥 try 블록에서 발생한 에러를 상위 메서드에서 catch 할 수 없다.

public static void main(String[] args) {
    try {
        String input = Input();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public static String Input() throws IOException {
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    try {
        throw new IllegalArgumentException();
//            return in.readLine();
    } finally {
        int i = 1 / 0;
        System.out.println("close check!");
        in.close();
    }
}

* 다음과 같이 Input 메서드의 try 블록에서 IllegalArgumentException이 발생하여도 fianlly 블록에서 발생한 ArithemticException을 상위 메서드에서 잡는다.

 

try-with-resources를 사용하자

위와 같은 try-finally 방식의 단점을 보완하기 위해 Java 7부터는 try-with-resources 방식을 지원한다.

try-with-resources를 사용하기 위해서는 AutoCloseable 인터페이스를 구현해야 하는데, 해당 인터페이스는 close 메서드 하나만을 정의해 놓은 간단한 인터페이스이다.

 

StringReader 같은 수 많은 자바 라이브러리 및 서드 파티 라이브러리에서는 이미 해당 AutoCloseable을 구현했거나 확장해 두었다.

 

하나의 자원을 처리하는 try-with-resources 방식은 다음과 같이 작성할 수 있다.

public static String Input() throws IOException {
    try (BufferedReader in = new BufferedReader(new InputStreamReader(System.in))) {
        return in.readLine();
    }
}

 

복수의 자원을 처리하는 try-with-resources 방식은 다음과 같이 작성할 수 있다.

public static void out() throws IOException {
    try (BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
         BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out))) {
        out.write(in.readLine());
    }
}

코드의 가독성이 매우 올라갔고, 실제 디버깅 결과 close()에서 멈추는 것을 확인할 수 있다.

 

또한 이전의 문제였던 try 내의 오류 역시 해결할 수 있다.

public static String Input() throws Exception {
    try (ExampleClass exampleClass = new ExampleClass()) {
        throw new IllegalArgumentException();
    }
}

static class ExampleClass implements AutoCloseable {

    @Override
    public void close() throws Exception {
        throw new NullPointerException();
    }
}

위와 같이, close() 과정에서 발생하는 NullPointerException 예외가 아닌, try 블록에서 발생하는 IllegalArgumentException이 발생하는 것을 알 수 있다. 추가로 NullPointerException이 무시되는 것이 아닌 Suppressed 태그 뒤에 출력되는 것을 확인 할 수 있다.

'이팩티브 자바' 카테고리의 다른 글

[Effective] equals & hashCode  (0) 2023.03.09
[Effective] 정적 팩터리 메서드  (0) 2023.03.02