Kotlin에서 함수는 최고 수준의 구성체로 데이터 타입으로 변수에 함수를 저장하고 저장된 함수를 다른 함수에 전달 및 그것을 반환할 수 있습니다.
이번에는 함수 혹은 람다식을 매개변수로 받거나 반환할 수 있는 고차함수를 다루는 방법에 대해 알아보겠습니다.
다음과 같은 경우는 고차함수에 포함되지 않습니다.
fun add(num1: Int, num2: Int): Int {
return num1 + num2
}
위 함수는 매개변수와 반환 타입을 Int형으로 정의하였습니다. 매개변수 혹은 반환 타입을 함수 타입으로 지정하지 않았기 때문 고차함수가 아닙니다.
그렇다면 고차함수는 어떻게 정의할 수 있을까요?
fun trick() {
println("No treats!")
}
특정 메세지를 출력하는 trick 함수를 정의하였습니다.
fun main() {
val trickFun = trick
}
main() 함수 본분에서 trickFun 변수를 만들어 trick 함수를 대입합니다. 함수 호출이 아닌 저장이 목적이기 때문에 trick 함수 다음에 괄호()를 사용하지 않습니다. 코드를 실행하면 컴파일러는 trick을 함수의 이름으로 인식하지만 변수에 함수가 할당되는 대신 함수가 호출될 것으로 예상하기 때문에 오류가 발생합니다.

함수를 값으로 참조하기 위해선 함수 참조 연산자 ::를 사용해야 합니다.
fun main() {
val trickFun = ::trick
}
다음과 같이 함수 참조 연산자를 사용하면 에러가 사라진 것을 확인할 수 있습니다.
람다 표현식은 fun 키워드 없이 함수를 정의할 수 있는 간결한 문법입니다. 람다식은 다른 함수에 관한 함수 참조 없이 변수에 직접 저장할 수 있습니다. 또한 다른 변수에 할당 및 새로운 변수의 이름으로 함수를 호출할 수 있습니다.
위 예제 코드를 변경해 보겠습니다.
fun main() {
val trickFun = trick
trick()
trickFun()
}
val trick = { println("No treats!") }
다음과 같이 작성하면 함수 참조 연산자 없이 trick() 함수를 참조할 수 있으며, ()를 사용하여 호출할 수 있습니다.
위에서 매개변수나 반환 타입으로 함수를 지정이 가능하다 설명하였습니다. 코틀린에서 이것이 어떻게 가능한지 알아보겠습니다.
함수의 타입은 매개변수 목록, →, 반환 타입이 포함된 괄호 쌍으로 구성되어있습니다.

위 trick 변수의 데이터 타입은 () → Unit 입니다. 함수에 매개변수가 없으므로 괄호 안은 비어있고, 아무것도 반환하지 않기 때문에 Unit입니다.
만약 두 개의 Int 매개변수를 사용하고 Int 타입을 반환하는 함수라면 이 함수의 데이터 타입은 (Int, Int) → Int 입니다
위 trick 함수의 데이터 타입을 명시적으로 지정해 봅시다.
val trick: () -> Unit = { println("...") }
이번엔 함수를 반환 타입으로 사용해 보겠습니다.
먼저 treat 변수와 trickOrTreat() 함수를 정의하겠습니다.
val trick = { println("No treats!") }
val treat = { println("Have a treat!") }
fun trickOrTreat(isTrick: Boolean): () -> Unit {
return if (isTrick) {
trick
} else {
treat
}
}
이제 main() 함수 본문에 이를 호출하여 출력 결과를 확인해보겠습니다.
fun main() {
val treatFun = trickOrTreat(false)
val trickFun = trickOrTreat(true)
treatFun()
trickFun()
}

함수를 반환 타입으로 지정 후 정상적으로 출력이 되는 것을 확인할 수 있습니다.
이제 함수를 다른 함수에 인수로 전달하는 방법에 대해 알아보겠습니다.
trickOrTreat() 함수를 수정하겠습니다.
fun trickOrTreat(isTrick: Boolean, extraTreat: (Int) -> String): () -> Unit {
return if (isTrick) {
trick
} else {
println(extraTreat(5))
treat
}
}
이제 main() 함수에서 trickOrTreat() 함수를 호출할 때 람다식으로 함수를 정의하고 extraTreat 매개변수에 전달해야 합니다.
fun main() {
val coins: (Int) -> String = { quantity ->
"$quantity quarters"
}
val cupcake: (Int) -> String = {
"Have a cupcake!"
}
val treatFunction = trickOrTreat(false, coins)
val trickFunction = trickOrTreat(true, cupcake)
treatFunction()
trickFunction()
}
람다식에서는 return 키워드를 사용할 수 없습니다. 람다식에선 마지막 표현식 결과가 반환 값이 됩니다.
그리고 코드를 실행하여 출력을 확인해 보겠습니다.

isTrick 값이 false일 경우에만 인자로 전달된 coins가 포함되어 출력되는 것을 확인할 수 있습니다.
마지막으로 null 조건을 활용하여 코드를 변경해 보겠습니다.
함수 타입도 다른 데이터 타입과 마찬가지로 null을 허용할 수 있습니다.
fun trickOrTreat(isTrick: Boolean, extraTreat: ((Int) -> String)?): () -> Unit {
return if (isTrick) {
trick
} else {
if (extraTreat != null) {
println(extraTreat(5))
}
treat
}
}
fun main() {
val coins: (Int) -> String = { quantity ->
"$quantity quarters"
}
val treatFunction = trickOrTreat(false, coins)
val trickFunction = trickOrTreat(true, null)
treatFunction()
trickFunction()
}

다음과 같이 출력 값 또한 동일한 것을 확인할 수 있습니다.
참고문서
좋은 정보 감사합니다 ^_ ^