본문 바로가기

Study/Kotlin Study

[Kotlin] 영역 함수

+ 항공대학교 김철기 교수님의 객체 지향 프로그래밍 과목 내용를 정리한 글입니다.

영역 함수 (Scope Function)

run, let, with apl, apply, also 라는 다섯가지 영역함수가 존재한다.

+ run - let - with, apply - also 간 유사성이 있다.

 

다섯 가지를 모두 능숙하게 사용할 필요는 없으나 유사성이 있는 영역함수 중 한 두개는 쓸 줄아는 것이 좋다.

 

다섯가지 영역 함수에 대한 코드를 읽을 줄은 알아야한다.


run 영역 함수

- 객체를 만들고 초기화하는 과정에서 많이 사용한다.

 

- run()의 인자인 람다함수는 수신 객체의 메소드처럼 정의한다.

=> 즉 this는 수신 객체이다.

 

- 마지막 문자의 반환값이 run()의 반환값이 된다.

 

- 수신 객체와 연관된 코드를 run 영역함수 내 람다함수로 모듈화하여 분리한다.

 

run 영역 함수를 사용하지 않은 코드

class Address {
    var city: String = ""
    var street: String = ""
    var house: String = ""
    fun post(message: String): String =
        "Message for ($city, $street, $house): $message"
}

fun main() {
    val addr = Address()
    addr.city = "London"
    addr.street = "Baker Street"
    addr.house = "221b"
    val msg = addr.post("Hello")
    println(msg) // Message for (London, Baker Street, 221b): Hello!
}

 

run 영역 함수를 사용한 코드

class Address {
    var city: String = ""
    var street: String = ""
    var house: String = ""
    fun post(message: String): String =
        "Message for ($city, $street, $house): $message"
}

fun main() {
    val msg = Address().run {
        // 이 영역 안에서는 Address 객체가 this가 된다.
        city = "London"
        street = "Baker Street"
        house = "221b"
        post("Hello!") // 마지막 줄의 내용이 반환 값이 된다.
    }
    println(msg) // Message for (London, Baker Street, 221b): Hello!
}

문맥이 없는 run 영역 함수

class Address(val city: String, val street: String, val house: String) {
    fun asText() = "$city, $street, $house"
}

fun main() {
    val city = readLine() ?: return
    val street = readLine() ?: return
    val house = readLine() ?: return
    val address = Address(city, street, house)

    println(address.asText()) // London, Baker Street, 221b
}

 

위 코드에서 main 함수의 첫 4줄은 address를 만들기 위한 코드이므로 영역 함수로 묶는 것이 바람직하다. (모듈화)

class Address(val city: String, val street: String, val house: String) {
    fun asText() = "$city, $street, $house"
}

fun main() {
    // 인스턴스 address를 run을 이용하여 생성
    val address = run() {
        val city = readLine() ?: return
        val street = readLine() ?: return
        val house = readLine() ?: return
        Address(city, street, house)
    }
    println(address.asText()) // London, Baker Street, 221b
}

With 영역 함수

- run 영역 함수와 비슷하지만, run 앞에 올 객체를 with의 첫 인자로 받는다.

 

앞 예제를 with 영역 함수로 바꾼 코드

class Address {
    var city: String = ""
    var street: String = ""
    var house: String = ""
    fun post(message: String): String =
        "Message for ($city, $street, $house): $message"
}

fun main() {
    val msg = with(Address()) {
        // 이 영역 안에서는 Address 객체가 this가 된다.
        city = "London"
        street = "Baker Street"
        house = "221b"
        post("Hello!") // 마지막 줄의 내용이 반환 값이 된다.
    }
    println(msg) // Message for (London, Baker Street, 221b): Hello!
}

let 영역 함수

- let 함수는 앞쪽에 오는 수신 객체가 뒤 람다 함수의 첫 번째 파라미터로 작용한다.

class Address {
    var city: String = ""
    var street: String = ""
    var house: String = ""
    fun post(message: String): String =
        "Message for ($city, $street, $house): $message"
}

fun main() {
    val msg = Address().let {
        it.city = "London"
        it.street = "Baker Street"
        it.house = "221b"
        it.post("Hello!")
    }
    println(msg) // Message for (London, Baker Street, 221b): Hello!
}

 

위 코드에서 it 대신 addr이라는 변수에 인스턴스를 할당하여 사용할 수 있다.

class Address {
    var city: String = ""
    var street: String = ""
    var house: String = ""
    fun post(message: String): String =
        "Message for ($city, $street, $house): $message"
}

fun main() {
    val msg = Address().let {addr ->
        addr.city = "London"
        addr.street = "Baker Street"
        addr.house = "221b"
        addr.post("Hello!")
    }
    println(msg) // Message for (London, Baker Street, 221b): Hello!
}

let을 이용한 널 안전성(?.) 처리

import java.lang.NumberFormatException

// 정수가 아닌 값이 들어오면 null을 반환한다.
fun readInt() = try {
    readLine()?.toInt()
} catch (e: NumberFormatException) {
    null
}

fun main() {
    val arr = intArrayOf(3, 9, 7, 6, 4)
    val num = readInt()

    val result = if(num != null) arr.getOrNull(num) else null
    // getOrNull: num이 arr의 인덱스를 벗어난 수라면 null, 아니라면 num에 해당하는 수를 반환한다.

    if(result != null) {
        println(result)
    }
}

 

위 코드에서 result값을 설정할 때, if문을 사용하지 않으면 오류가 발생한다.

=> let 영역 함수를 이용하여 널 안정성을 보장하는 코드를 만들 수 있다.

import java.lang.NumberFormatException

// 정수가 아닌 값이 들어오면 null을 반환한다.
fun readInt() = try {
    readLine()?.toInt()
} catch (e: NumberFormatException) {
    null
}

fun main() {
    val arr = intArrayOf(3, 9, 7, 6, 4)
    val num = readInt()

    val result = num?.let {num -> arr.getOrNull(num)}
    // Int? 형 num 인스턴스를 null이 아니라고 간주하고, Int형 인자로 받는다. (it 사용 가능)

    if(result != null) {
        println(result)
    }
}

apply, also 영역 함수

- apply는 run에 대응하지만 최종적으로 수신 객체를 반환한다.

class Address {
    var city: String = ""
    var street: String = ""
    var house: String = ""
    fun post(message: String): String =
        "Message for ($city, $street, $house): $message"
}

fun main() {
    val msg = Address().apply {
        // 이 영역 안에서는 Address 객체가 this가 된다.
        city = "London"
        street = "Baker Street"
        house = "221b"
    }.post("Hello!")
    // apply로 반환된 Address 인스턴스의 post함수를 호출하여 msg에 저장한다.

    println(msg) // Message for (London, Baker Street, 221b): Hello!
}

 

- also는 let에 대응하지만 최종적으로 수신 객체를 반환한다.

class Address {
    var city: String = ""
    var street: String = ""
    var house: String = ""
    fun post(message: String): String =
        "Message for ($city, $street, $house): $message"
}

fun main() {
    val msg = Address().also {
        // 이 영역 안에서는 Address 객체가 this가 된다.
        it.city = "London"
        it.street = "Baker Street"
        it.house = "221b"
    }.post("Hello!")
    // also로 반환된 Address 인스턴스의 post함수를 호출하여 msg에 저장한다.

    println(msg) // Message for (London, Baker Street, 221b): Hello!
}

'Study > Kotlin Study' 카테고리의 다른 글

[Kotlin] 하위 클래스 선언  (2) 2023.10.23
[Kotlin] Fragment 실습  (2) 2023.10.14
[Kotlin] 확장  (0) 2023.10.09
[Kotlin] 함수형 프로그래밍  (0) 2023.09.22
[Kotlin] 객체 (Object)  (0) 2023.09.17