고차함수(High-Order Function)
매개변수로 함수를 전달받거나 함수를 반환하는 함수를 말합니다.
고차 함수 내에서 매개변수로 함수 타입을 선언
하고 고차 함수를 호출하는 곳에서 람다 함수를 전달
하는 구조 입니다.
아래는 Int 타입 매개변수 두 개를 전달받아 결과로 Int 타입의 데이터를 반환하는 일반함수와
매개변수로 함수를 받는 예시 입니다.
ex)
fun normalFun(a: Int, b: Int): Int = a + b
//매개변수 b는 Int형 매개변수 하나와 반환값 타입이 Int형인 함수를 의미합니다.
fun highFun(a: Int, b: (Int) -> Int) {
val result = b(10)
println("a = $a, b = $result")
}
// ....
highFun(10, { x -> x * x})
고차 함수와 함수 타입 매개변수
함수 타입의 매개변수 대입
- 고차 함수의
마지막 매개변수가 함수 타입이면 함수를 호출할 때 ()를 생략
할 수 있습니다.
ex)
fun hoFun(argFun: (Int) -> Int) {
// ...
}
fun hoFun2(a: Int, argFun1: (Int) -> Int, argFun: (Int) -> Boolean) {
// ...
}
hoFun({ x -> x * x})
hoFun { x ->
x * x
}
hoFun2(10, { it + it}, { it > 10})
hoFun2(10, { it + it}) {
it > 10
}
함수 타입 기본값 이용
- 일반적인 함수의 매개변수에 기본값을 지정하듯
고차함수에서도 기본값을 선언할 수 있습니다.
ex)
fun hoFun(a: Int, b: (Int) -> Int = {x -> x * x }) {
// ...
}
hoFun(a) // 기본값 이용
hoFun(a) {
it * it
}
고차 함수와 함수 반환
- 고차 함수에서 함수를 반환하는 방법은 반환 타입에 함수 타입을 선언 하면 됩니다.
ex)
fun hoFun(str: String) : (a: Int, b: Int) -> Int {
return when (str) {
"-" -> {a,b -> a-b}
"*" -> {a,b -> a*b}
"/" -> {a,b -> a/b}
else -> {a,b -> a+b}
}
}
val resultFun = hoFun("*")
resultFun(10,5)
함수 참조와 익명 함수 이용
- 일반적으로 고차 함수에서 매개변수나 반환값으로 람다 함수를 많이 이용하지만,
함수 참조나 익명 함수를 이용해도 됩니다.
함수 참조를 이용한 함수 전달
- 함수 참조 연산자 콜론 두 개(::) 를 이용하여
다른 함수를 고차 함수의 인자로 이용
하는 방법입니다.
fun hoFun(a: (Int) -> Int) {
// ...
}
fun someFun(x: Int): Int {
retunr x * 10
}
// ...
hoFun {
it * 10
}
hoFun(::someFun)
익명 함수를 이용한 함수 전달
- 람다식에는 반환을(return) 명시적으로 해줄 수 없기 때문에 주로 반환을 명시적으로 선언하려고 할 때 이용됩니다.
ex)
// 익명 함수는 단어 그대로 이름이 없는 함수를 뜻하며 기존 함수 선언에서 이름이 빠진 형식이다.
val anonyFun = fun(a: Int): Int = a * a
val anonyFun2 = fun(a: Int): Int {
println("it's anonymous function")
return a + a
}
fun hoFun(x: (Int) -> Int){
val result = x(10)
println("it's High order Function")
}
// ...
hoFun(anonyFun)
hoFun(anonyFun2)
코틀린 API의 유용한 고차함수
run()
inline fun <R> run(block: () -> R): R
inline fun <T, R> T.run(block: T.() -> R): R
-
단순히 람다 함수를 실행하고 그 결괏값을 얻는 목적으로 사용합니다.
- 매개변수에 함수 타입이 있으므로
람다 함수를 전달해 주고 해당 람다 함수의 반환값을 그대로 반환
하는 구조
- 매개변수에 함수 타입이 있으므로
-
객체의 멤버에 접근할때 유용하게 사용됩니다.
- run()를 호출한 객체가 람다 함수에 전달되어 객체의 멤버를 바로 이용할 수 있습니다.
ex)
data class User(
var name: String,
var age: Int,
) {
fun sayName() {
println("name = $name")
}
fun checkAdult(): Boolean = age >= 20
}
val result : Int = run {
println("it's run call")
10 + 20 // 마지막 반환
}
val isAdult : Boolean = User("Lee", 27).run {
sayName()
checkAdult()
}
apply()
inline fun <T> T.apply(block: T.() -> Unit): T
- apply()함수는 apply()함수를 호출한
객체를 반환
합니다.
ex)
data class User(
var name: String,
var age: Int,
) {
fun sayName() {
println("name = $name")
}
fun checkAdult(): Boolean = age >= 20
}
//...
val user = User("Lee", 27).apply {
name = "kim"
age = 19
sayName()
if (checkAdult())
println("성인입니다.")
else
println("성인이 아닙니다.")
}
let()
inline fun <T, R> T.let(block: (T) -> R): R
-
호출한 객체(
T
)를 매개변수인 람다 함수(위의block
)에 매개변수(T
)로 전달후 해당 람다 함수의 반환값(R
)을 그대로 반환(R
) 합니다. -
변수 선언을 줄이고자 할 때 사용이 용이합니다.
data class User(
val name: String,
val age: Int
)
// ...
User("Lee",27).let {
println(it.name)
println(it.age)
}
with() 함수
inline fun <T, R> with(receiver: T, block: T.() -> R): R
- run() 함수와 유사 하며 차이점은 run()함수는 자신을 호출한 객체를 이용하지만, with()함수는 매개변수로 지정한 객체를 이용합니다.
data class User(
var name: String,
var age: Int,
) {
fun sayHello() {
println("hello")
}
}
val user = User("lee", 27)
with(user) {
name = "kim"
sayHello()
}
// 동일한 동작
uer.run {
name = "kim"
sayHello()
}
인라인 함수
인라인 함수란?
- 고차 함수에 람다 함수를 전달하고 람다 함수를 이용하는 코드가 많아져서 런타임 때 성능 문제가 발생할 수 있기에, 컴파일 단계에서 inline으로 정의한 함수의 내용이 호출되는 곳에 정적으로포함 되므로 런타임 때 함수 호출이 그만큼 줄고 성능에 도움이 됩니다.
참조 : 깡샘의 코틀린 프로그래밍