프로그래밍 언어/코틀린

코틀린 24. 스코프함수

닉네임못짓는사람 2021. 1. 6. 10:11
반응형

이번 글에서는 스코프함수에 대해서 알아보도록 하겠습니다.

스코프(Scope)


스코프함수에 대해 알아보기 전에 앞서 스코프가 뭔지 먼저 알아야겠죠?

스코프는 사전 뜻 그대로 범위를 말하는데, 패키지나 클래스, 함수 등의 내부에서 형성되는 범위를 말합니다.

 

예를 들어 패키지 A가 있다고 생각해보도록 합시다.

이 패키지 안에는 클래스 B와 함수 C가 있는데, 이 둘은 모두 패키지 내부의 하나의 큰 스코프 안에 속합니다.

그리고 클래스 B안에 있는 함수나 변수 등은 또 다른 스코프를 이루고, 함수 C에 있는 멤버들 또한 또 다른 스코프를 이룹니다.

이런 스코프의 특징은 같은 스코프 내의 다른 멤버들에게 자유롭게 접근 가능하다는 점과

상위 스코프의 멤버를 하위 스코프 내에서 재정의하여 사용할 수 있다는 점입니다.

 

전자의 경우 저번에 패키지에 대해서 알아봤을 때, 다른 파일에 있지만 같은 패키지 내에 있는 함수를

자유롭게 호출했던 예제가 이에 속하게됩니다.

 

후자의 경우 상위 스코프, 예를 들어 클래스 멤버로 a라는 변수가 있을 때 이 클래스 내에 있는

하위 스코프인 함수 스코프 내에서 이 멤버를 재정의하여 사용할 수 있습니다.

여기서 재정의는 클래스 멤버 a를 바꿔버린다는 뜻이 아니고 같은 이름의 또 다른 멤버를 함수 내에서 정의한다는 뜻입니다.

fun main() {
    var ex = A()
    println(ex.a)
    ex.B()
}

class A(){
    var a = 10
    fun B(){
	var a = 20
        println(a)
    }
}

위와 같이 클래스 A에 멤버변수 a를 선언하고, 함수 내에서 다시 a라는 변수를 선언했을 때,

두 변수는 이름은 같지만 서로 다른 값을 가지는 독립적인 변수가 됩니다.

또한 스코프 사용 시에는 자신과 같거나 상위 스코프의 멤버에는 접근할 수 있지만, 하위 스코프의 멤버에는 접근할 수 없습니다.

스코프 함수


그럼 이제 스코프 함수 이야기로 들어가 봅시다.

스코프 함수는 람다함수를 사용해서 객체에 접근해 함수를 수행하는 함수들을 이야기합니다.

스코프 함수에는 apply, run, with, let, also의 5가지가 있는데, 기본적으로는 모두 비슷한 동작을 수행합니다.

 

1. apply

apply는 람다함수를 사용해 함수를 실행시키고, 객체를 반환하는 동작을 수행합니다.

fun main() {
    var obj = A(10, 60).apply{
        a = 30
    }
    println("${obj.a}, ${obj.b}")
}

class A(var a: Int, var b: Int){
    
}

위와 같이 obj라는 A클래스의 인스턴스를 생성할 때, apply함수를 사용하여 이 인스턴스의

변수 a를 30으로 바꿔주고, 그 결과인 객체를 obj변수에 넘겨주게 됩니다.

 

2. run

run 또한 람다함수를 사용해 동작을 수행하는데, apply와는 다르게 기존 람다함수처럼 코드블럭의 마지막 줄을 반환해줍니다.

fun main() {
    var obj = A(10, 60).apply{
        a = 30
    }
    println(obj.run{
        println("a: ${a}, b: ${b}")
        b
    })
}

class A(var a: Int, var b: Int){
    
}

위와 같이 obj객채에 접근해 a와 b의 값을 출력시켜주면서, 마지막 줄에 있는 b의 값을 반환한 것을 볼 수 있습니다.

 

3. with

with함수는 run함수와 동일한 기능을 하지만, run함수가 객체에 참조연산자를 붙여서 사용하는 반면에

with함수는 객체를 parameter로 받아 실행시킨다는 차이점만 가지고 있습니다.

fun main() {
    var obj = A(10, 60).apply{
        a = 30
    }
    println(with(obj){
        println("a: ${a}, b: ${b}")
        b
    })
}

class A(var a: Int, var b: Int){
    
}

 

4. let

let함수도 run함수와 기본적으로는 동일한 동작을 하지만, it이라는 키워드를 사용해서

해당 객체의 멤버를 참조할 수 있다는 특징이 있습니다.

마치 함수 내에서 this를 사용해 클래스 멤버에 접근하는 것과도 같은데, 왜 이렇게 사용하는지는 코드를 보고 확인합시다.

fun main() {
    var b = 50
    var obj = A(10, 60).apply{
        a = 30
    }
    println(with(obj){
        println("a: ${a}, b: ${b}")
        b
    })
}

class A(var a: Int, var b: Int){
    
}

위의 코드를 보면 main함수의 가장 위에 b라는 변수를 새로 선언하고 50으로 초기화했습니다.

이후의 코드는 동일한데, 본래 60으로 출력되었어야 할 b가 50으로 출력된 것을 볼 수 있습니다.

 

이는 사실 obj의 b를 출력한 것이 아니고 main함수의 b를 출력했기 때문인데요.

run함수와 with함수를 사용할 경우 이렇게 객체의 멤버보다 상위 스코프의 멤버를 우선시하게 됩니다.

이런 때 let과 it을 사용하면 두 멤버를 혼동하는 일 없이 올바르게 사용할 수 있습니다.

fun main() {
    var b = 50
    var obj = A(10, 60).apply{
        a = 30
    }
    println(obj.let{
        println("a: ${it.a}, b: ${it.b}")
        it.b
    })
}

class A(var a: Int, var b: Int){
    
}

let을 사용하면 객체의 멤버에 접근할 때, 앞에 it을 무조건 붙여주어야 합니다.

 

5. also

also함수는 let함수처럼 내부에서 it키워드를 사용할 수 있는데, 맨 처음 apply처럼 객체를 반환합니다.

fun main() {
    var b = 50
    var obj = A(10, 60).apply{
        a = 30
    }
    obj.also{
        it.a = 50
        it.b = 100
        b = 1
    }
    println("a: ${obj.a}, b: ${obj.b}, main의 b: ${b}")
}

class A(var a: Int, var b: Int){
    
}

위의 코드에서 also와 it을 사용해 main함수의 b와 obj객체의 b를 구분한 것을 볼 수 있습니다.

 

이렇게 이번 글에선 스코프 함수에 대해서 알아봤습니다.

스코프 함수에 대한 코틀린 홈페이지 글은 아래의 링크에서 확인하실 수 있습니다.

kotlinlang.org/docs/reference/scope-functions.html

 

Scope Functions - Kotlin Programming Language

 

kotlinlang.org

이번 글은 이 정도로 마치도록 하겠습니다.

감사합니다.

반응형

'프로그래밍 언어 > 코틀린' 카테고리의 다른 글

lateinit과 by lazy의 차이  (0) 2021.12.28
코틀린 25. 오브젝트  (0) 2021.01.07
코틀린 23. 고차함수, 람다함수  (0) 2021.01.05
코틀린 22. 맵(Map)  (0) 2021.01.01
코틀린 21. 셋(Set)  (0) 2020.12.30