๊นจ์•Œ ๊ฐœ๋… ๐Ÿ“‘/Kotlin

[Kotlin] invoke() ์—ฐ์‚ฐ์ž

interfacer_han 2026. 10. 10. 09:09

#1 invoke()

#1-1 ์˜ˆ์‹œ ์ฝ”๋“œ

class Greeter {
    operator fun invoke() {
        println("Hello")
    }
}

val greeter = Greeter()
greeter()

์ฝ”๋“œ๋ฅผ ๋ณด๊ณ  ํ˜ผ๋ž€์Šค๋Ÿฌ์› ๋‹ค. ์ด๊ฒŒ ๋ฌด์Šจ ์ง“์ด์ง€? ๊ฐ‘์ž๊ธฐ ๊ฐ์ฒด์— ๊ด„ํ˜ธ๋ฅผ ์™œ ๋ถ™์ด์ง€? ์ƒ์„ฑ์ž ๊ฐ™์€ ๊ฑด๊ฐ€? ํ•˜์ง€๋งŒ ๊ทธ๋Ÿด ๋ฆฌ๊ฐ€ ์—†๋‹ค. ์ƒ์„ฑ์ž๋Š” ํด๋ž˜์Šค์— ๊ด„ํ˜ธ๋ฅผ ๋ถ™์ด๋Š” ๊ฑฐ๊ณ , ์ด ๊ฒฝ์šฐ๋Š” ๊ฐ์ฒด์— ๊ด„ํ˜ธ๋ฅผ ๋ถ™์ด๋Š” ๊ฑฐ๋‹ˆ๊นŒ. ์• ์ดˆ์— '์ด๋ฏธ ์ƒ์„ฑ'๋œ ์กด์žฌ์ธ ๊ฐ์ฒด๋ฅผ ๋˜ ์ƒ์„ฑํ•œ๋‹ค? ๋ง๋„ ์•ˆ ๋œ๋‹ค.

 

#1-2 What

์ •์ฒด

์‚ฌ์‹ค greeter()๋Š” greeter.invoke()์™€ ๊ฐ™์€ ์ฝ”๋“œ๋‹ค. ์ฆ‰, invoke()๋ผ๋Š” ํ•จ์ˆ˜์˜ ์ถ•์•ฝ ํ˜•ํƒœ๊ฐ€ ()์ธ ๊ฒƒ์ด๋‹ค. ์ด๋Š” invoke๊ฐ€ ์—ฐ์‚ฐ์ž(operater)์ด๊ธฐ์— ๊ฐ€๋Šฅํ•œ ํŠน๊ถŒ์ ์ธ ๋ฌธ๋ฒ•์ด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด "invoke๊ฐ€ ๋Œ€์ฒด ๋ญ๊ธธ๋ž˜ ๊ด„ํ˜ธ ์—ฐ์‚ฐ์ž๋ฅผ ์ฐจ์ง€ํ•˜๊ณ  ์žˆ๋Š”๊ฐ€?"๋ผ๊ณ  ๋ฌป์ง€ ์•Š์„ ์ˆ˜๊ฐ€ ์—†๋‹ค.

 

์˜๋ฏธ

๋จผ์ €, invoke์˜ ์˜์–ด ๋‹จ์–ด์  ๋Šฌ์•™์Šค๋ฅผ ์•Œ์•„์•ผ ํ•œ๋‹ค. invoke๋Š” "ํ˜ธ์ถœํ•˜์—ฌ ์ž‘๋™์‹œํ‚ค๋‹ค"๋ผ๋Š” ์˜๋ฏธ๋‹ค. ํ•˜์ง€๋งŒ call์ฒ˜๋Ÿผ '๋ถ€๋ฅธ๋‹ค'๋ผ๋Š” ๋А๋‚Œ๊ณผ๋Š” ์‚ด์ง ๋‹ค๋ฅด๋‹ค. "invoke a right"๋ผ๋Š” ๋ฌธ์žฅ์„ ๋ฒˆ์—ญํ•˜๋ฉด "๊ถŒ๋ฆฌ๋ฅผ ํ–‰์‚ฌํ•˜๋‹ค"๊ฐ€ ๋˜๋Š”๋ฐ, ์ด์ฒ˜๋Ÿผ '์ด๋ฏธ ์กด์žฌํ•˜๋Š” ๊ถŒ๋ฆฌ๋ฅผ ๋Œ์–ด์™€ ํ–‰์‚ฌ'ํ•œ๋‹ค๋Š” ๋А๋‚Œ์ด๋‹ค. ๊ทธ๋ž˜์„œ invoke๋Š” ๊ฐ์ฒด ์ž์ฒด๊ฐ€ ํ•˜๋‚˜์˜ ๋™์ž‘์„ ๋Œ€ํ‘œํ•˜๋„๋ก ํ‘œํ˜„(์„ค๊ณ„)ํ•˜๋Š” ์šฉ๋„๋กœ ์“ฐ์ธ๋‹ค.

 

๊ฒฐ๋ก 

invoke๋Š” ๋ฐ˜๋“œ์‹œ ์ง€์ผœ์•ผ ํ•  ๊ณ ์ •๋œ ์“ฐ์ž„์ด ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๊ณ , (์—ฌ๋А ๋‹ค๋ฅธ ์ฝ”๋“œ๋“ค์ด ๊ทธ๋ ‡๋“ฏ) ํ”„๋กœ๊ทธ๋ž˜๋จธ ์ž…๋ง›๋Œ€๋กœ ๋œฏ์–ด๊ณ ์น  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ์ผ ๋ฟ์ด๋‹ค. ๋‹ค๋งŒ, ์˜๋ฏธ(๋Šฌ์•™์Šค)๋ฅผ ๊ณ ๋ คํ•  ๋•Œ ๊ฐ์ฒด์˜ ์ •์ฒด์„ฑ๊ณผ ์—ฐ๊ฒฐ๋˜๋Š” ๊ธฐ๋ณธ ๋™์ž‘์„ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์ด ์ž์—ฐ์Šค๋Ÿฝ๋‹ค.

 

#1-3 ์˜ˆ์‹œ

class ValidatePassword {
    operator fun invoke(password: String): Boolean {
        return password.length >= 8
    }
}
val validatePassword = ValidatePassword()
val isValid = validatePassword("12345678")

์˜๋ฏธ์ƒ, invoke์— ์ € ๋™์ž‘์„ ํ• ๋‹นํ•œ ๊ฒƒ์€ ์ž์—ฐ์Šค๋Ÿฝ๋‹ค.

 

#1-4 ์˜์˜: ๊ฐ์ฒด๋ฅผ ํ•จ์ˆ˜์ฒ˜๋Ÿผ

๋งˆ์น˜ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋“ฏ, ๊ฐ์ฒด์— ()๋ฅผ ๋ถ™์ธ๋‹ค... ๊ทธ๋ ‡๋‹ค, ๊ฐ์ฒด๋ฅผ ํ•จ์ˆ˜์ฒ˜๋Ÿผ ํ˜ธ์ถœํ•˜๊ณ  ์žˆ๋‹ค. invoke()๋Š” ๊ทธ ๊ฐ์ฒด๋ฅผ ๋Œ€ํ‘œํ•˜๋Š” ๋™์ž‘์„ ๊ตฌํ˜„ํ•ด ๋‘๋Š” ๊ฒŒ ์˜๋ฏธ์ƒ์œผ๋กœ ์˜ณ๊ณ , ์‹ค์ œ ๊ตฌํ˜„ ๊ด€๋ก€๋„ (๋‹น์—ฐํžˆ) ๊ทธ๋Ÿฌํ•˜๋‹ค. ๊ฒฐ๊ตญ ๊ทธ๋ ‡๊ฒŒ ๊ตฌํ˜„๋œ invoke๋Š” ์ด๋ ‡๊ฒŒ ํ˜•ํƒœ์™€ ๊ธฐ๋Šฅ์˜ ๊ฒฝ๊ณ„๋ฅผ ํ—ˆ๋ฌผ๊ณ , ์˜๋ฏธ(="๊ฐ์ฒด, ๋„ˆ์˜ ๋Œ€ํ‘œ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•ด!")์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋„์™€์ฃผ๋Š” ์…ˆ์ด๋‹ค.

 

#2 invoke()์™€ ํ•จ์ˆ˜ ํƒ€์ž…

#2-1 ๋จผ์ € ์•Œ์•„๋‘˜ ๊ฐœ๋…

  • ํ•จ์ˆ˜ ํƒ€์ž…(Fuction types)์— ๋Œ€ํ•ด ์ž˜ ๋ชจ๋ฅธ๋‹ค๋ฉด ๋จผ์ € ์ด ๊ฒŒ์‹œ๊ธ€์„ ์ฝ์ž.
  • ๋žŒ๋‹ค(Lambda) ํ‘œํ˜„์‹์— ๋Œ€ํ•ด ์ž˜ ๋ชจ๋ฅธ๋‹ค๋ฉด ๋จผ์ € ์ด ๊ฒŒ์‹œ๊ธ€์„ ์ฝ์ž.

 

#2-2 ์˜ˆ์‹œ ์ฝ”๋“œ

val add: (Int, Int) -> Int = { a, b ->
    a + b
}

/*
~ 3๋ถ€๋ถ„์œผ๋กœ ์ฝ”๋“œ ์ชผ๊ฐœ๊ธฐ ~

[1] add
    ๋ณ€์ˆ˜ ์ด๋ฆ„
[2] (Int, Int) -> Int
    ํ•จ์ˆ˜ ํƒ€์ž…
[3] { a, b -> a + b }
    ๋ณ€์ˆ˜์— ์ €์žฅ๋˜๋Š” ๋žŒ๋‹ค (ํ•จ์ˆ˜์˜ ๋‚ด์šฉ)
*/

invoke์™€ ํ•จ์ˆ˜ ํƒ€์ž…์˜ ๊ด€๊ณ„๋ฅผ ์„ค๋ช…ํ•˜๊ธฐ ์œ„ํ•œ ์˜ˆ์‹œ ์ฝ”๋“œ๋‹ค. ์ด ์ฝ”๋“œ๋Š” ์ฃผ์„์— ์ •๋ฆฌํ•œ ๋Œ€๋กœ, ํฌ๊ฒŒ 3๋ถ€๋ถ„์œผ๋กœ ๋‚˜๋‰œ๋‹ค.

 

#2-3 ๊ณต์‹ ๋ฌธ์„œ ์ฝ๊ธฐ

ํ•จ์ˆ˜ ํƒ€์ž… ๊ด€๋ จ

ํ•จ์ˆ˜ ํƒ€์ž…์„ ์„ค๋ช…ํ•˜๋Š” ๊ณต์‹ ๋ฌธ์„œ์— ์•„๋ž˜์˜ ์ˆ˜์‹์ด ๋‚˜์˜จ๋‹ค (์†”์งํžˆ ๊ณต์‹ ๋ฌธ์„œ์—์„œ ์™œ ์ˆ˜์‹์œผ๋กœ ์„ค๋ช…ํ•˜๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค. ํ—ท๊ฐˆ๋ฆฌ๊ธฐ๋งŒ ํ•œ๋‹ค).

FT(A1, ..., An) → R ≡ FunctionN[A1, ..., An, R]

์ด๋Š” ํ•จ์ˆ˜ ํƒ€์ž… (A1, ..., An) -> R์ด FunctionN ๊ณ„์—ด๊ณผ ๋™์ผํ•˜๋‹ค๋Š” ๋ง์ด๋‹ค (FunctionN์ด ๋ฌด์—‡์ธ์ง€๋Š” ํ›„์ˆ ํ•จ).

 

invoke() ๊ด€๋ จ

๊ทธ๋ฆฌ๊ณ  invoke()๋ฅผ ์„ค๋ช…ํ•˜๋Š” ๊ณต์‹ ๋ฌธ์„œ์—๋Š”, ํ•จ์ˆ˜ ํƒ€์ž… ๋ณ€์ˆ˜๋ฅผ invoke()๋ฅผ ํ†ตํ•ด ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค("A value of a function type can be invoked by using its invoke(...) operator: f.invoke(x) or just f(x).")๊ณ  ์ ํ˜€ ์žˆ๋‹ค.

 

๊ณต์‹ ๋ฌธ์„œ ์กฐํ•ฉํ•˜๊ธฐ

2๊ฐ€์ง€ ๊ณต์‹ ๋ฌธ์„œ๋ฅผ ์กฐํ•ฉํ•˜๋ฉด, ์˜ˆ์‹œ ์ฝ”๋“œ(#2-2)์—์„œ ๋ณด์•˜๋˜ ํ•จ์ˆ˜ ํƒ€์ž… (Int, Int) -> Int๋Š” ๊ฐœ๋…์ ์œผ๋กœ ์•„๋ž˜์˜ ์ธํ„ฐํŽ˜์ด์Šค์— ๋Œ€์‘๋œ๋‹ค ํ•  ์ˆ˜ ์žˆ๋‹ค.

interface Function2<P1, P2, R> {
    operator fun invoke(p1: P1, p2: P2): R
}

์‹ค์ œ ๋‚ด๋ถ€ ๊ตฌ์กฐ๊ฐ€ ์ •ํ™•ํ•˜๊ฒŒ ์ด ๋ชจ์Šต์ด๋ผ๋Š” ๊ฒƒ์€ ์•„๋‹ˆ๊ณ , ์–ด๋””๊นŒ์ง€๋‚˜ ๊ฐœ๋…์ ์œผ๋กœ ๊ทธ๋ ‡๋‹ค๋Š” ์–˜๊ธฐ๋‹ค.

 

#2-4 ๊ฒฐ๋ก 

/*
val add: (Int, Int) -> Int = { a, b ->
    a + b
}

์™€ ๊ฐ™์€ ์ฝ”๋“œ
↓ ↓ ↓
*/

val add = object : Function2<Int, Int, Int> {
    override fun invoke(a: Int, b: Int): Int {
        return a + b
    }
}

add(1, 2) // OK
add.invoke(3, 4) // OK

์ฆ‰, ํ•จ์ˆ˜ ํƒ€์ž…์€ ๋‚ด๋ถ€์ ์œผ๋กœ invoke() ์„ ์–ธ์œผ๋กœ ๊ตฌํ˜„๋œ๋‹ค. ๊ทธ๋ž˜์„œ add.invoke()๋„ add()์™€ ๋˜‘๊ฐ™์ด ํ˜ธ์ถœ๋œ๋‹ค. ์กฐ๊ธˆ ๋” ์ •ํ™•ํ•˜๊ฒŒ ๋งํ•˜๋ฉด, ์šฐ๋ฆฌ๊ฐ€ ์ผ๋ฐ˜์ ์ด๋ฉฐ ๋‹น์—ฐํ•˜๊ฒŒ ์จ์˜ค๋˜ add()๊ฐ€ ์‹ค์€ add.invoke()์˜€๋‹ค!

 

#2-5 ์•ˆ์ „ ํ˜ธ์ถœ ์—ฐ์‚ฐ์ž(?.)

๊ตณ์ด add()๋กœ ์งง๊ฒŒ ์“ธ ์ˆ˜ ์žˆ๋Š” ๊ฑธ add.invoke()๋กœ ์“ฐ๋Š” ๊ฒฝ์šฐ๋Š” ๊ฑฐ์˜ ์—†๋‹ค. ํ•˜์ง€๋งŒ, ๋ฐ˜๋“œ์‹œ ์จ์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ๋‹ค. ๋ฐ”๋กœ ํ•จ์ˆ˜ ํ‘œํ˜„์‹์ด nullable์ธ ๊ฒฝ์šฐ๋‹ค.

// #2-2 ์† add์˜ ํ•จ์ˆ˜ ํ‘œํ˜„์‹์„ nullable๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค.
val nullableAdd: ((Int, Int) -> Int)? = { a, b ->
    a + b
}

// add์™€ ๋‹ฌ๋ฆฌ, nullableAdd๋Š” invoke์˜ ์ถ•์•ฝํ˜• ์‚ฌ์šฉ์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค.
nullableAdd?.() // X
nullableAdd?.invoke() // O

nullable ๋ฐ์ดํ„ฐใ†ํ•จ์ˆ˜ ํƒ€์ž…์˜ ์ง๊ฟ์€ ?.(์•ˆ์ „ ํ˜ธ์ถœ ์—ฐ์‚ฐ์ž, Safe Call Operator)๋‹ค (์—ฌ๋‹ด์œผ๋กœ, ์ด์™€ ๋น„์Šทํ•œ ๋ชจ์–‘์ธ ?: ์—ฐ์‚ฐ์ž๋Š” ์—˜๋น„์Šค ์—ฐ์‚ฐ์ž๋ผ๊ณ  ๋ถ€๋ฅธ๋‹ค).

 

aaa?.bbb ๊ตฌ์กฐ์—์„œ bbb๋Š” aaa๊ฐ€ null์ด ์•„๋‹Œ ๊ฒฝ์šฐ์—๋งŒ (์ปดํŒŒ์ผ๋Ÿฌ ์ž…์žฅ์—์„œ) ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค. ๋งŒ์•ฝ null์ด๋ผ๋ฉด ์ฝ”๋“œ๋ฅผ ๊ฑด๋„ˆ๋›ด๋‹ค. ์–ด์จŒ๋“  nullable ๋ณ€์ˆ˜์˜ ํŠน์ • ๊ธฐ๋Šฅ์„ ์“ธ ๊ฑฐ๋ผ๋ฉด ?.์„ ๋ถ™์—ฌ์ฃผ์–ด์•ผ ํ•œ๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด nullableAdd?.()์™€ ๊ฐ™์€ ๋ชจ์–‘์€ ๊ฐ€๋Šฅํ• ๊นŒ? ์•„์‰ฝ๊ฒŒ๋„ ์•„๋‹ˆ๋‹ค. ?.๋’ค์— ()๋ฅผ ๋ถ™์—ฌ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฌธ๋ฒ• ์ž์ฒด๋ฅผ ์ฝ”ํ‹€๋ฆฐ์—์„œ ์ง€์›ํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ฆ‰ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ์ถ•์•ฝํ˜•์ด ์•„๋‹Œ, ์˜ค๋ฆฌ์ง€๋„ invoke()๋ฟ์ธ ์…ˆ์ด๋‹ค.

 

#3 SAM๊ณผ์˜ ๋น„๊ต

#3-1 ๋จผ์ € ์•Œ์•„๋‘˜ ๊ฐœ๋…

ํ•จ์ˆ˜ํ˜• ์ธํ„ฐํŽ˜์ด์Šค(Single Abstract Method Interface, SAM)์— ๋Œ€ํ•ด ์ž˜ ๋ชจ๋ฅธ๋‹ค๋ฉด ๋จผ์ € ์ด ๊ฒŒ์‹œ๊ธ€์„ ์ฝ์ž.

 

#3-2 ๊ณตํ†ต์ 

๋‘˜ ๋‹ค ๊ฐ„๊ฒฐํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ์„ ์œ„ํ•ด ์‚ฌ์šฉ๋œ๋‹ค. ์—†๋‹ค๊ณ  Kotlin์ด ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋กœ์„œ์˜ ๊ธฐ๋Šฅ์„ ์ƒ์‹คํ•˜์ง€๋Š” ์•Š๊ฒ ์ง€๋งŒ, Kotlin ํ”„๋กœ๊ทธ๋ž˜๋จธ๋“ค์€ ์ƒ๋‹นํžˆ ๊ท€์ฐฎ์•„์งˆ ๊ฒƒ์ด๋‹ค.

 

invoke๋Š” ๊ฐ์ฒด๋ฅผ ๋Œ€ํ‘œํ•˜๋Š” ๋ฉ”์†Œ๋“œ์˜ ์ด๋ฆ„์„ ๊ธฐ์–ตํ•  ํ•„์š”๊ฐ€ ์—†๊ฒŒ ํ•ด์ฃผ๊ณ , SAM์€ ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€ ์ง€๋‹Œ ๋‹จ์ผ ๋ฉ”์†Œ๋“œ์˜ ์ด๋ฆ„์„ ๊ธฐ์–ตํ•  ํ•„์š”๊ฐ€ ์—†๊ฒŒ ํ•ด์ค€๋‹ค.

 

๋˜, ๋‘˜ ๋‹ค ๋žŒ๋‹ค ํ‘œํ˜„์‹๊ณผ ๊ด€๊ณ„๊ฐ€ ์žˆ๋‹ค. #2์—์„œ ์„ค๋ช…ํ•œ ๋Œ€๋กœ, invoke๋Š” ํ•จ์ˆ˜ ํƒ€์ž…์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ง€๋‹ˆ๋Š” ํ•จ์ˆ˜๋‹ค. ๊ทธ๋ฆฌ๊ณ  SAM์€ ๋žŒ๋‹ค๋ฅผ ํ†ตํ•ด ๊ตฌํ˜„ํ•œ๋‹ค.

 

#3-3 ์ฐจ์ด์ 

์šฉ๋„๊ฐ€ ๋‹ค๋ฅด๋‹ค. invoke๋Š”, ์ข€ ๋” ์ •ํ™•ํžˆ ๋งํ•ด์„œ invoke()๋ฅผ ์ถ•์•ฝํ•œ ()๋Š” ๋ฉ”์†Œ๋“œ ํ˜ธ์ถœ ๋ฌธ๋ฒ•์„ ๊ฐ„์†Œํ™”ํ•œ๋‹ค (#1-4๋ฅผ ์ƒ๊ฐํ•˜๋ฉด, ๊ฐ์ฒด ํ˜ธ์ถœ ๋ฌธ๋ฒ•์„ ๊ฐ„์†Œํ™”ํ•œ๋‹ค๊ณ  ๋งํ•ด๋„ ์–ดํ๋Š” ์•„๋‹ˆ๋‹ค). ๋ฐ˜๋ฉด, SAM Conversion์€ ๊ฐ์ฒด ์ƒ์„ฑ์„ ๊ฐ„์†Œํ™”ํ•˜๋Š” ์šฉ๋„๋กœ ์“ฐ์ธ๋‹ค.