관리 메뉴

나만을 위한 블로그

[Kotlin] use 확장 함수 알아보기 본문

개인 공부/Kotlin

[Kotlin] use 확장 함수 알아보기

참깨빵위에참깨빵_ 2023. 7. 27. 21:42
728x90
반응형

예전에 이펙티브 코틀린을 읽으면서 use를 써서 리소스를 닫으라는 내용을 봤었다.

 

https://onlyfor-me-blog.tistory.com/489

 

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

더 이상 필요하지 않을 때 close()를 써서 명시적으로 닫아야 하는 리소스가 있다. Input/OutputStream java.sql.Connection java.io.Reader(FileReader, BufferedReader, CSSParser) java.new.socket, java.util.Scanner 이런 리소스들

onlyfor-me-blog.tistory.com

 

요약하면 Input/OutputStream, Scanner 등의 요소들은 더 이상 사용하지 않는다면 close()를 통해 리소스를 거둬들이는 게 좋다는 내용이다.

그러나 use라는 게 무엇이길래 사용하라고 한 것일지 궁금했고, 마침 동영상을 서버로 업로드하는 로직을 구현하는 과정에서 InputStream을 사용하게 됐는데 이 기회에 알아보고자 포스팅한다.

 

코틀린 공식문서에서 말하는 use와 구현 상세는 아래와 같다.

 

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io/use.html

 

use - Kotlin Programming Language

 

kotlinlang.org

이 리소스에서 지정된 블록 함수를 실행한 다음 예외 발생 여부에 상관없이 올바르게 종료한다

 

구현 코드는 안드로이드 스튜디오에서 확인한 내용을 가져왔다. 추가로 매개변수와 리턴값에 대해서도 설명하고 있어서 이것도 가져왔다.

 

Params : block - 이 Closeable 자원을 처리하는 함수
Returns : 이 리소스에서 호출된 블록 함수의 결과
@InlineOnly
@RequireKotlin("1.2", versionKind = RequireKotlinVersionKind.COMPILER_VERSION, message = "Requires newer compiler version to be inlined correctly.")
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    var exception: Throwable? = null
    try {
        return block(this)
    } catch (e: Throwable) {
        exception = e
        throw e
    } finally {
        when {
            apiVersionIsAtLeast(1, 1, 0) -> this.closeFinally(exception)
            this == null -> {}
            exception == null -> close()
            else ->
                try {
                    close()
                } catch (closeException: Throwable) {
                    // cause.addSuppressed(closeException) // ignored here
                }
        }
    }
}

 

확인해 보면 알겠지만 use 함수는 Closeable이란 파일 안에 위치하고 있다. 이건 또 뭔가?

 

https://developer.android.com/reference/kotlin/java/io/Closeable

 

Closeable  |  Android Developers

 

developer.android.com

닫을 수 있는 데이터의 소스 또는 대상이다. 객체가 갖고 있는 리소스(열린 파일 등)를 해제하기 위해 close 메서드가 호출된다

close() : 이 스트림을 닫고 연결된 모든 시스템 리소스를 해제한다

 

Closeable은 WebSocketReader, Http2Connection, FileIntputStream 등의 클래스가 구현하고 있는 인터페이스다. 이 인터페이스의 구현체를 사용한다면 위에서 말했듯 사용 후 close()를 호출하는 코드가 필요하다.

그러나 객체마다 close()를 호출해야 하고 혹시 모를 예외를 막기 위해 try-catch로 감싸서 처리해야 하는데, 그렇게 되면 코드 가독성도 떨어지고 쓸데없이 긴 줄을 차지하게 된다. 이 때 use를 사용하면 close() 호출 처리를 생략하면서 깔끔한 코드를 작성할 수 있다.

아래는 간단한 예시다.

 

fun main() {
    val url = "https://www.naver.com/"
    val destination = File("downloaded_data.txt")

    downloadFromWeb(url, destination)

    println("Data downloaded to ${destination.absolutePath}")
}

fun downloadFromWeb(url: String, destination: File) = URL(url).openStream().use { input ->
    destination.outputStream().use { output ->
        input.copyTo(output)
    }
}

 

이 코드를 실행하면 네이버 메인 화면을 구성하는 html 태그들을 가져와서 txt 파일에 저장한다. 실행하면 해당 파일이 저장된 경로가 콘솔에 표시될 것이다.

 

 

내 경우 이 프로젝트의 루트 폴더에 downloaded_data.txt 파일이 저장됐다. 직접 실행해서 확인해 보자.

downloadFromWeb()이 주목할 함수다. 이 함수의 첫 부분인 URL(url).openStream() 호출을 통해 InputStream을 만들고, destination.outputStream()을 통해 다운받은 데이터를 파일에 쓸 때 사용할 OutputStream을 만든다.

이를 통해 각 스트림마다 use 확장함수를 사용하기 때문에 close()를 2번 호출할 필요 없이 아주 간단하게 스트림들을 닫고 프로그램을 종료할 수 있다.

안드로이드에서도 이미지, 동영상을 다룰 때 InputStream과 OutputStream을 다뤄야 할 때가 종종 있다. 이 때 try-catch 대신 use 확장함수를 써서 처리해 보는 것도 좋을 것이다.

반응형
Comments