DEV Community

dss99911
dss99911

Posted on • Originally published at dss99911.github.io

Kotlin 함수와 람다: 함수 선언부터 고차 함수까지

Kotlin은 함수형 프로그래밍을 강력하게 지원합니다. 이 포스트에서는 함수와 람다의 다양한 사용법을 알아보겠습니다.

함수 선언

기본 함수

fun sum(a: Int, b: Int): Int {
    return a + b
}

// 사용
val result = sum(1, 2)
Enter fullscreen mode Exit fullscreen mode

기본 인자 (Default Arguments)

fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) { ... }
Enter fullscreen mode Exit fullscreen mode

가변 인자 (Vararg)

fun foo(vararg strings: String) { /* ... */ }

// 배열 전개
fun(*array)
Enter fullscreen mode Exit fullscreen mode

Unit 반환 타입

반환 값이 없는 함수는 Unit을 반환합니다. 생략 가능합니다.

fun printSum(a: Int, b: Int): Unit {
    println("sum of $a and $b is ${a + b}")
}

// Unit 생략 가능
fun printSum(a: Int, b: Int) {
    println("sum of $a and $b is ${a + b}")
}
Enter fullscreen mode Exit fullscreen mode

Single-Expression 함수

함수 본문이 단일 표현식인 경우 중괄호를 생략할 수 있습니다.

fun double(x: Int): Int = x * 2

// 반환 타입 추론
fun double(x: Int) = x * 2

fun max(a: Int, b: Int) = if (a > b) a else b
Enter fullscreen mode Exit fullscreen mode

Named Arguments

파라미터 이름을 명시하여 호출할 수 있습니다.

reformat(str, wordSeparator = '_')

// Spread operator
foo(strings = *arrayOf("a", "b", "c"))
Enter fullscreen mode Exit fullscreen mode

Infix 함수 (중간 연산자)

// 선언
infix fun Int.shl(x: Int): Int { ... }

// 사용
1 shl 2      // infix notation
1.shl(2)     // 일반 호출
Enter fullscreen mode Exit fullscreen mode

Global 함수

클래스 외부에 선언된 함수는 다른 파일에서 import 없이 사용 가능합니다 (import static과 유사).

Tail Recursive 함수

재귀 함수 최적화를 위해 tailrec 키워드를 사용합니다. JVM에서만 지원됩니다.

tailrec fun findFixPoint(x: Double = 1.0): Double =
    if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))
Enter fullscreen mode Exit fullscreen mode

함수 내 함수

함수 내부에 다른 함수를 정의할 수 있습니다.

fun outer() {
    fun inner() { ... }
    inner()
}
Enter fullscreen mode Exit fullscreen mode

백틱 함수명

테스트에서 읽기 쉬운 이름을 사용할 수 있습니다.

fun `should return true when valid input`() {
    // test code
}
Enter fullscreen mode Exit fullscreen mode

Inline 함수

람다를 인라인으로 처리하여 오버헤드를 줄입니다.

inline fun <T> lock(lock: Lock, body: () -> T): T { ... }

// noinline: 특정 람다는 인라인하지 않음
inline fun foo(inlined: () -> Unit, noinline notInlined: () -> Unit) { ... }
Enter fullscreen mode Exit fullscreen mode

crossinline

람다가 다른 컨텍스트에서 호출될 때 사용합니다.

inline fun f(crossinline body: () -> Unit) {
    val f = object : Runnable {
        override fun run() = body()
    }
}
Enter fullscreen mode Exit fullscreen mode

Inline 프로퍼티

val foo: Foo
    inline get() = Foo()

var bar: Bar
    get() = ...
    inline set(v) { ... }

inline var bar: Bar
    get() = ...
    set(v) { ... }
Enter fullscreen mode Exit fullscreen mode

Reified 타입

inline 함수에서 제네릭 타입 정보를 런타임에 사용할 수 있습니다.

inline fun <reified T> TreeNode.findParentOfType(): T? {
    var p = parent
    while (p != null && p !is T) {
        p = p.parent
    }
    return p as T?
}

// 사용
treeNode.findParentOfType<MyTreeNode>()
Enter fullscreen mode Exit fullscreen mode
object YamlReader {
    inline fun <reified T> read(path: String): T =
        ObjectMapper(YAMLFactory()).readValue(File(path), T::class.java)
}
Enter fullscreen mode Exit fullscreen mode

람다 표현식

람다 타입 선언

인터페이스 선언 없이 함수 타입을 정의할 수 있습니다.

val lambda: (String) -> String

val sum: (Int, Int) -> Int = { x, y -> x + y }

// 익명 함수
ints.filter(fun(item) = item > 0)
Enter fullscreen mode Exit fullscreen mode

함수를 파라미터로 받기

fun <T> lock(lock: Lock, body: () -> T): T {
    lock.lock()
    try {
        return body()
    } finally {
        lock.unlock()
    }
}

// 함수 참조로 전달
fun toBeSynchronized() = sharedResource.operation()
val result = lock(lock, ::toBeSynchronized)
Enter fullscreen mode Exit fullscreen mode

람다 사용법

// 명시적 파라미터
{ arg -> arg + 1 }

// 암시적 파라미터 (it)
fruits
    .filter { it.startsWith("a") }
    .sortedBy { it }
    .map { it.toUpperCase() }
    .forEach { println(it) }

// 다중 파라미터
max(strings, { a, b -> a.length < b.length })

// 사용하지 않는 변수
map.forEach { _, value -> println("$value!") }
Enter fullscreen mode Exit fullscreen mode

람다와 return

람다 내부의 return은 해당 람다를 포함한 함수를 종료시킵니다 (inline 함수의 경우).

// 암시적 반환 (마지막 표현식이 반환값)
ints.filter {
    val shouldFilter = it > 0
    shouldFilter
}

// 명시적 반환
ints.filter {
    val shouldFilter = it > 0
    return@filter shouldFilter
}

// 라벨 사용
ints.forEach lit@ {
    if (it == 0) return@lit
    print(it)
}

// 암시적 라벨
ints.forEach {
    if (it == 0) return@forEach
    print(it)
}
Enter fullscreen mode Exit fullscreen mode

Destructuring 람다

{ a -> ... }           // 단일 파라미터
{ a, b -> ... }        // 두 파라미터
{ (a, b) -> ... }      // 구조 분해 (Pair 등)
{ (a, b), c -> ... }   // 구조 분해 + 추가 파라미터
Enter fullscreen mode Exit fullscreen mode

Receiver가 있는 람다

fun <T> T.apply(block: T.() -> Unit): T { /* ... */ }

class HTML {
    fun body() { ... }
}

fun html(init: HTML.() -> Unit): HTML {
    val html = HTML()
    html.init()
    return html
}

// 사용
html {
    body()  // receiver 객체의 메서드 직접 호출
}
Enter fullscreen mode Exit fullscreen mode

표준 라이브러리 함수

let

it을 통해 값을 참조하고 결과를 반환합니다.

val mapped = value?.let { transformValue(it) } ?: defaultValueIfValueIsNull
Enter fullscreen mode Exit fullscreen mode

apply

객체의 메서드를 호출하고 객체 자체를 반환합니다.

IntArray(size).apply { fill(-1) }
Enter fullscreen mode Exit fullscreen mode

with

한 객체의 여러 메서드를 호출할 때 유용합니다.

class Turtle {
    fun penDown()
    fun penUp()
    fun turn(degrees: Double)
    fun forward(pixels: Double)
}

val myTurtle = Turtle()
with(myTurtle) {
    penDown()
    for (i in 1..4) {
        forward(100.0)
        turn(90.0)
    }
    penUp()
}
Enter fullscreen mode Exit fullscreen mode

also

객체로 각기 다른 작업을 수행할 때 사용합니다.

val numbers = mutableListOf(1, 2, 3)
numbers.also { println("The list elements: $it") }
    .add(4)
Enter fullscreen mode Exit fullscreen mode

run

with와 유사하지만 확장 함수 형태입니다.

val result = listOf.run { add(1); get(0) }
Enter fullscreen mode Exit fullscreen mode

takeIf

조건에 맞는 경우에만 처리합니다.

val number = Random.nextInt(100)
val evenOrNull = number.takeIf { it % 2 == 0 }
Enter fullscreen mode Exit fullscreen mode

Sequence 빌더

무한 시퀀스를 생성할 수 있습니다.

fun main(args: Array<String>) {
    val fibonacciSeries = sequence {
        var a = 0
        var b = 1
        yield(a)
        yield(b)

        while (true) {
            val c = a + b
            yield(c)
            a = b
            b = c
        }
    }

    println(fibonacciSeries.take(10).joinToString(","))
}
Enter fullscreen mode Exit fullscreen mode

컬렉션 함수

리스트 생성

listOf("apple", "banana", "kiwi")     // 불변 리스트
mutableListOf(1, 2, 3)                 // 가변 리스트
Enter fullscreen mode Exit fullscreen mode

다음 단계

함수와 람다에 대해 알아보았습니다. 다음으로 제어문에 대해 알아보세요.


Originally published at https://dss99911.github.io

Top comments (0)