본문 바로가기
개발새발/코틀린

[코틀린/Kotlin] 스코프 함수

by 조희우 2025. 8. 21.

스코프 함수

  • 객체의 컨텍스트 내에서 코드 블록을 실행 할 수 있게하는 함수
  • 특정 객체에 대하여 임시로 이름 없이 작업을 수행할 때 유용

대표적인 스코프 함수

함수명 수신 객체 이름 반환값 주사용 목적
let it 람다 결과값 null 체크, 임시 변수 범위 제한, 체이닝
run this 람다 결과값 객체 구성 + 결과 반환
with this 람다 결과값 외부 객체 구성 (확장 X)
apply this 객체 자기 자신 객체 구성 (초기화 등)
also it 객체 자기 자신 부수 작업 수행 (로깅, 디버깅 등)

주요 스코프 함수 예시

let

  • null 체크
  • 임시 변수 범위 제한
val name: String? = "희우"

name?.let {
	println("이름은 ${it} 입니다.") // null이 아닐 경우 실행
}

run

  • 객체 구성
  • 최종 결과 리텅
val textLength = "희우입니다.".run{
	println("문자열 길이는 ${length}")
	length
}

with

  • 외부 객체를 인자로 전달
val sb = StrinbBuilder()
val result = with(sb) {
	append("안녕")
	append(" Kotlin")
	toString()
}

apply

  • 객체 구성 후 자기 자신 반환
val user = User("희우", 24).apply{
	age += 1
}

also

  • 디버깅, 로그 등 부수 효과
val list = mutableListOf("A", "B").also{
	pritnln("초기 리스트: $it")
	it.add("C")
}

문제

1. let을 사용하여 null 체크와 출력이 동시에 되도록 코드를 구현하시오.

 

답:

val name: String? = "희우"
// name이 null이 아니면 "이름은 희우입니다." 출력

name?.let{
	println("이름은 $it입니다.")
}

 

 

2. 변수 input이 빈 문자열이 아니라면 길이를 출력하는 코드를 run을 사용해 작성하시오.

 

[조건]

  • run을 반드시 사용
  • 빈 문자열이면 아무 것도 출력하지 않아야함

답:

val input = "Hello"
// input이 빈 문자열이 아니면, "입력의 길이: 5" 출력

input.run{
	println("입력의 길이: ${this.length}")
}

보충:

  • 빈 문자열일 경우가 빠짐
input.takeIf { it.isNotEmpty() }?.run{
	println("입력의 길이: ${length}")
}

 

 

3. 다음 User 클래스는 이름과 나이를 저장하는 데이터 클래스이다.

data class User(var name: String, var age: Int)

아래 코드의 빈칸을 채워 새로운 User 인스턴스를 만들고, 이름은 "희우", 나이는 24로 설정한 뒤 생성된 유저의 정보를 로그로 출력하는 코드를 구현하시오.

val user = User("", 0).____ {
    name = "희우"
    age = 24
}.____ {
    println("새로 생성된 유저: $it")
}

[조건]

  • 첫 번째 빈칸에는 객체의 속성을 설정할 수 있는 스코프 함수를,
  • 두 번째 빈칸에는 디버깅용 로그를 출력하는 데 적합한 스코프 함수를 넣으세요.

답:

val user = User("", 0).with {
    name = "희우"
    age = 24
}.also {
    println("새로 생성된 유저: $it")
}


보충:

  • with는 객체를 인자로 받는 일반함수로 점으로 직접 이어서 사용 불가능
  • apply는 객체 자체(this)를 리시버로 받아서 값을 설정하고 그 객체를 반환하므로 점으로 이어서 사용 가능
  • also는 객체 자체(it)를 인자로 받아 부가작업하기 적합
val user = User("", 0).apply {
    name = "희우"
    age = 24
}.also {
    println("새로 생성된 유저: $it")
}

 

 

4. let, apply, also, run, with 중 하나를 적절히 골라서 문제를 구현하시오.

val userInput: String? = readLine()
// userInput이 null 아니고, 길이가 5 이상이면 대문자로 변환 후 
// "입력: (변환된 문자열)" 형태로 출력, 아니면 "입력이 짧거나 없습니다." 출력하는 코드를 작성해봐.

[요구사항]

  • userInput이 null이 아니고 공백이 아니면 uppercase()로 대문자로 바꿔서 출력
  • 스코프 함수를 꼭 사용해서 작성

[힌트]

  • null 체크 → ?.let 또는 takeIf
  • 출력은 println

 

답:

userInput
	?.takeIf{ it.isNotEmpty() && it.length >= 5 }
	?.run{
		println("입력: ${this.uppercase()}")
	} ?: println("입력이 짧거나 없습니다.")

 

 

5. 아래 조건에 맞추어 내용을 작성하시오

[조건]

  1. User 객체를 생성할 때, name은 빈 문자열, age는 0으로 초기화
  2. apply, also, let, run, with 중 적절한 스코프 함수를 골라서 구현
  3. 스코프 함수를 활용해 아래 작업을 연쇄적으로 수행
  • name을 "희우"로 변경
  • age를 24로 변경
  • "User 정보: 이름=희우, 나이=24" 를 출력

 

답: 

data class User(var name: String, var age: Int)

val user = User("", 0)

user
.apply{
	name = "희우"
	age = 24}
.also{
	println("User 정보: 이름=${it.name}, 나이=${it.age}")
}

 

 

6. 아래 코드를 let을 사용해서 구현하시오.

fun printName(name: String?) {
    if (name != null) {
        println("이름은 ${name}입니다")
    }
}

 

답:

fun printName(name: String?){
	name?.let{
		println("이름은 $it입니다.")
	}
}

 

 

7. 아래 코드를 run을 활용해서 객체 생성과 초기화, 동작 수행을 한 번에 수행하게 구현하시오.

data class User(var name: String, var age: Int)

fun createUser(): User {
    val user = User("홍길동", 20)
    user.age += 1
    return user
}

 

답:

data class User(var name: String, var age: Int)

fun createUser(): User{
	val user = User("홍길동", 20)
		.apply{
			age += 1
		}
	return user		
}				

 

보충:

1. 표현식 함수로 작성하기

fun createUser(): User = User("홍길동", 20).apply { age += 1}

 

2. run을 사용하기

더보기
더보기
더보기

run / apply

run과 apply 모두 객체 자신을 참조하지만 반환값이 다르다.

  • run: 블록 마지막 식의 결과
  • apply: 객체 자신(this)
fun createUser(): User {
    return User("홍길동", 20).run {
        age += 1
        this  // run 블록에서 변경한 객체 자신을 반환
    }
}