관리 메뉴

나만을 위한 블로그

[이펙티브 코틀린] 아이템 9. use를 써서 리소스를 닫아라 본문

책/Effective Kotlin

[이펙티브 코틀린] 아이템 9. use를 써서 리소스를 닫아라

참깨빵위에참깨빵 2022. 6. 2. 23:33
728x90
반응형

더 이상 필요하지 않을 때 close()를 써서 명시적으로 닫아야 하는 리소스가 있다.

 

  • Input/OutputStream
  • java.sql.Connection
  • java.io.Reader(FileReader, BufferedReader, CSSParser)
  • java.new.socket, java.util.Scanner

 

이런 리소스들은 AutoCloseable을 상속받는 Closeable 인터페이스를 구현하고 있다.

이런 모든 리소스는 최종적으로 리소스에 대한 레퍼런스가 없어질 때 가비지 컬렉터가 처리하지만 굉장히 느리고 그 동안리소스를 유지하는 비용이 많이 들어간다. 따라서 더 이상 필요없다면 명시적으로 close()를 호출하는 게 좋다.

전통적으로 이런 리소스는 try-catch 블록을 써서 처리한다.

 

fun countCharactersInFile(path: String): Int {
    val reader = BufferedReader(FileReader(path))
    try {
        return reader.lineSequence().sumBy { it.length }
    }   finally {
        reader.close()
    }
}

 

이런 코드는 복잡하고 좋지 않다. 리소스를 닫을 때 예외가 발생할 수도 있는데 이런 예외를 따로 처리하지 않기 때문이다.

또한 try 블록, finally 블록 안에서 오류가 발생하면 둘 중 하나만 전파된다. 둘 다 전파될 수 있으면 좋겠지만 이걸 직접 구현하려면 코드가 길고 복잡해진다. 그래도 많이 쓰이는 일반적인 구현이라 표준 라이브러리에 use란 함수로 포함돼 있다.

use()를 써서 앞의 코드를 바꾸면 아래와 같다.

 

fun countCharactersInFile(path: String): Int {
    val reader = BufferedReader(FileReader(path))
    reader.use { 
        return reader.lineSequence().sumBy { it.length }
    }
}

 

람다 매개변수로 리시버(현재 코드에선 reader)가 전달되는 형태도 있으므로 줄여서 아래처럼 쓸 수도 있다.

 

fun countCharactersInFile(path: String): Int {
    BufferedReader(FileReader(path)).use { reader ->
        return reader.lineSequence().sumBy { it.length }
    }
}

 

파일을 리소스로 쓰는 경우가 많고 파일을 한 줄씩 읽어들이는 경우도 많으므로 코틀린 표준 라이브러리는 파일을 한 줄씩 처리할 때 활용될 수 있는 useLines()도 제공한다.

 

fun countCharactersInFile(path: String): Int {
    File(path).useLines { lines ->
        return lines.sumBy { it.length }
    }
}

 

이렇게 처리하면 메모리에 파일 내용을 한 줄씩만 유지하므로 대용량 파일도 적절하게 처리 가능하지만 파일의 줄을 단 한번만 쓸 수 있다는 단점이 있다. 파일의 특정 줄을 2번 이상 반복 처리하려면 파일을 2번 이상 열어야 한다. 앞의 코드는 아래처럼 간단히 쓸 수 있다.

 

fun countCharactersInFile(path: String): Int =
    File(path).useLines { lines ->
        lines.sumBy { it.length }
    }

 

반응형
Comments