Kotlin - { Let, Also, Apply, Run, With }

SeungHyuk Shin·2022년 1월 26일
0
post-thumbnail

코프링으로 개발하다가 접한 헷갈리는 5가지 Scope Functions을 정리 하기위해 작성했습니다.

Scope Functions

객체를 사용할 때 Scope(범위, 영역) 를 일시적으로 만들어서 속성(property)나 함수를 처리하는 용도로 사용되는 함수이다. 간편한 코드 사용과 가독성, 빌더 패턴의 이용, 부가적인 후처리 등을 하는 데에 도움을 준다.


1. Let

class bar:{
    private var number: Int? = null
    
    fun foo(){
    	if(number != null){
            val number2 = number + 1 // 👈 여기서 intellij가 오류를 보여준다.
        }
    }
}

해당 코드에서 오류가 나오는 이유는 우리가 number를 global하게 선언했기 때문에, if문은 통과한 직후 다른 스레드에 의해 number의 값이 다시 null이 될 수 있기 때문이다. number!!선언을 통해 당장 보여지는 오류는 없앨 수 있지만 실제로 null이 될 수 있는 확률은 여전히 존재하기 때문에 number가 null이 되면 우리 어플리케이션은 excption을 던질 것이다.

코드를 다음과 같이 수정해보자.

class bar:{
    private var number: Int? = null
    
    fun foo(){
    	val x = number?.let {
            val number2 = it + 1
            number2
        }
    }
}

다음과 같이 수정하면 오류도 아나고 다른 쓰레드에 의해 값이 변경될 수도 없다. 왜 그럴까? let 함수안에 it은 number가 let으로 호출됐을때의 값을 가지고 있기 때문에 코틀린은 let안의 구문에서는 number가 null이 될 수 없음을 알 수 있다.

만약 number가 null일 경우 다른 값을 주고 싶으면 엘비스 연산자를 이용해서 값을 할당 할 수 있다.

class bar:{
    private var number: Int? = null
    
    fun foo(){
    	val x = number?.let {
            val number2 = it + 1
            number2
        } ?: 3
    }
}

2. Also

다음은 AlSo다. Also는 Let과 매우 비슷한데, 다른점은 Also는 Let과 마지막 라인을 리턴하지 않는 대신, 수신 객체를 리턴한다. 예를 들어 우리가 제곱을 한다음 "또한" +1 을 하고 싶다고 가정해보자.

class bar:{
    private var number: Int = 0
    
    fun foo() = (number * number).also{ number++ }
}

여기서 또한 이라는 글자에 주목을 한다면 Also의 뜻꽈 똑같이 함수가 하는일이 많이 와닿을 수 있다. Let만큼 자주 쓰는 함수는 아니지만 특정 상황에서 매우 유용하게 사용 할 수 있는 함수이다.


3. Apply

Appliy 함수는 스코프 함수나 객체, 모두 사용할 수 있다. 특히 객체를 수정할 때 매우 유용한 함수이다. 그러므로 특정 객체에 수정을 많이 하고싶다면 Apply를 쓰는것을 추천한다.

class bar:{
    val intent = Intent().apply {
    	putExtra("", "")
        putExtra("", 0)
    }
}

apply 함수안에서는 this 객체로 받기때문에 it.putExtra 이런식으로 쓰지 않고, 그냥 객체 안에 있는 함수를 호출 할 수 있다. 만약에 Person 클래스를 수정하고 싶다면 다음과 같이 할 수 있다.

class bar:{
    var shin: Person = Person(name = "Shin", age = 20)
    
    // apply를 안 쓸 경우
    shin.name = "SeungHyuk"
    shin.age = 21
    
    // apply를 쓸 경우
    val shin = shin.apply {
    	name = "SeungHyuk"
        age = 21
    }
}

4. Run

Run은 객체를 받아 사용하지만 Let과 같이 마지막 라인을 리턴한다. 따라서 만약에 위와 같은 코드에 Run을 사용한다면 다음과 같이 사용해야 한다.

class bar:{
    var shin: Person = Person(name = "Shin", age = 20)
    
    val shin = shin.run {
    	name = "SeungHyuk"
        age = 21
        this
    }
}

run은 확장 함수가 아니고, 블럭에 입력값도 없다. 따라서 객체를 전달받아서 속성을 변경하는 형식에 사용되는 함수가 아니다. 이 함수는 어떤 객체를 생성하기 위한 명령문을 블럭 안에 묶음으로써 가독성을 높이는 역할을 한다.


5. with

with은 Run과 똑같은 역할을 하지만, 표현 방법은 다르다. with는 non-null의 객체를 사용하고 블럭의 return 값이 필요하지 않을 때 사용한다. 그래서 주로 객체의 함수를 여러 개 호출할 때 그룹화하는 용도로 활용된다.

class bar:{
    var shin: Person = Person(name = "Shin", age = 20)
    
    with(shin) {
    	...
    }
}

0개의 댓글