#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์ ๊ฐ์ฒด ์์ฑ์ ๊ฐ์ํํ๋ ์ฉ๋๋ก ์ฐ์ธ๋ค.
'๊นจ์ ๊ฐ๋ ๐ > Kotlin' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [Kotlin] Coroutines Flow - Flow.combine()๊ณผ Flow.stateIn() (0) | 2024.08.29 |
|---|---|
| [Kotlin] Coroutines Flow - StateFlow (0) | 2024.08.17 |
| [Kotlin] Coroutines Flow - Intermediate operator (0) | 2024.08.16 |
| [Kotlin] Coroutines Flow - Back pressure์ ๊ทธ ์ฒ๋ฆฌ (0) | 2024.08.15 |
| [Kotlin] Coroutines Flow - ๊ธฐ์ด (1) | 2024.08.01 |