본문 바로가기
개발 지식/Kotlin

[Kotlin] Kotlin 기초 - 04 (스코프 함수)

by 에르주 2022. 7. 17.
반응형

스코프 함수

함수형 언어의 특징을 좀 더 편리하게 사용할 수 있도록 기본 제공하는 함수이다.

인스턴스를 스코프 함수 내에서 활용하게 되면 인스턴스 내의 속성이나 함수를 깔끔하게 쓸 수 있다. (Framework와 유사기능)

 

스코프 함수에는

  • apply
  • run
  • with
  • also
  • let
  • ....

과 같이 다양한 함수가 있다. 예시 코드로 하나씩 살펴보자.

 

1) apply

: 인스턴스를 생성 한 후 변수에 담기 전 초기화 과정을 수행할 때 많이 쓰인다.

 

보통 코틀린에서 클래스 및 클래스 객체 및 함수를 활용할 경우에는 다음과 같이 쓰인다.

public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}


class Coffee(var name: String, var price: Int) {
    fun discount() {
        price -= 500
    }
}

var coffee = Coffee("아메리카노", 2500)
coffee.name = "[포장할인]" + coffee.price
coffee.discount()


// java 버젼
@Getter
class Coffee {

    private String name;
    private int price;
    
    public Coffee(String name, int price) {
        this.name = name;
        this.price = price;
        return new Coffee(this.name, this.price);
    }
    
    public discount() {
    	this.price -= 500;
    }
}
Coffee coffee = new Coffee("아메리카노", 2500)
String coffee_name = "[포장할인]" + coffee.getName();
coffee.discount()

 

여기서 [포장할인] 서두를 붙이기 위해서는 속성과 함수를 이용해야 한다. apply는 참조 연산자 없이 활용할 수 있다. 왜냐하면 apply는 인스턴스 자신을 반환하기 때문이다.

 

다음과 같이 활용할 수 있다.

coffee = Coffee("아메리카노", 2500).apply {
    name = "[포장할인]$name"
    discount()
}

// java 버전에서는 apply 역할을 하는 메소드를 작성해야 한다.
public Coffee apply() {
	
    String name = "[포장할인]" + this.name;
    discount(); 
    return new Cofffe(name, this.price);
}

 

2) run

: 이미 인스턴스가 만들어진 후에 인스턴스의 함수나 속성 scope 내에서 사용했을 때 유용하다. -> 예를 들어 자바 생성자 호출 후 해당 생성자에 관한 메소드나 변수값을 핸들링 할 때 유용한다.

@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

coffee.run {
    println("상품명: ${name}, 가격: ${price}원")

    this.name = name.replace("아메리카노", "피자 토스트 세트")
    price  = 4500
}

coffee.run {
    println("상품명: ${name}, 가격: ${price}원")
}

// 결과
// 상품명: [포장할인]아메리카노, 가격: 2000원
// 상품명: [포장할인]피자 토스트 세트, 가격: 4500원


// java 버전에서는 run 역할을 하는 메소드를 작성해야 한다.
public Coffee apply() {
	
    String name = "[포장할인]" + this.name;
    discount(); 
    return new Cofffe(name, this.price);
}

public Coffee run() {
	
	println("상품명 : " + this.name +", 가격: "+ this.price)

    String name = this.name.replace("아메리카노", "피자 토스트 세트");
    this.price = 4500

    return new Coffee(this.name, this.price);
}

public class main() {

	Coffee coffee = new Coffee();
    coffee = coffee.run()
    println("상품명 : " + coffee.getName() +", 가격: "+ coffe.getPrice())
}

 

 

3) with

: run과 동일한 기능, 차이점은 인스턴스를 참조 연산자 대신 파라미터로 받는다.

with(coffee) {
    println("상품명: ${name}, 가격: ${price}원")
}
// 결과
// 상품명: [포장할인]피자 토스트 세트, 가격: 4500원

//java 버전
public Coffee run(Coffee coffee) {
	println("상품명 : " + coffee.getName() +", 가격: "+ coffe.getPrice())
}

 

 

4) also

: also를 활용하면 it을 통해서 인스턴스 표현 가능 및 인스턴스로 반환한다.

// also 
public inline fun <T> T.also(
    block: (T) -> Unit
): T

var vanillaCoffee = coffee.also {
    it.name = it.name.replace("피자 토스트 세트", "바닐라 라떼")
    "coffee also 실행" // 무시 됌
}

vanillaCoffee.run {
    println("상품명: ${name}, 가격: ${price}원")
}

// 결과
// 상품명: [포장할인]햄치즈 토스트 세트, 가격: 4000원

 

인스턴스로 반환하므로 coffee.also에서 also 안의 String 인 "coffee also 실행" 하는 부분은 무시한다.

 

 

5) let

: let 또한 also와 같이 it을 통해서 인스턴스 표현이 가능하지만 인스턴스를 반환하는 것이 아닌 최종값을 반환한다.

// also
public inline fun <T, R> T.let(
    block: (T) -> R
): R

var cafeLatte = coffee.let {
    it.name = it.name.replace("피자 토스트 세트", "카페라떼")
    it.price = 3000
    "coffee also 실행 -> cafeLatte"
}

cafeLatte.run {
    println("상품명: ${name}, 가격: ${price}원")
}

println(cafeLatte)

// 결과
// coffee let 실행 -> cafeLatte

name 없대!

왜냐하면 let은 최종값으로 반환하므로 Coffee 객체가 아닌 String으로 반환한다.

즉 "coffee let 실행 -> cafeLatte"로 반환된 것이다.

 

실행 결과

 

끝.

반응형

댓글