코틀린 Functions

김성준·2022년 7월 20일

Functions

코틀린에서 함수는 fun 키워드를 사용하여 선언할 수 있습니다.

fun helloWorld() {
	println("Hello World")
}

Parameters

fun printString(str: String) {
	println(str)
}

함수의 파라미터는 (변수명: 타입)의 형태로 작성할 수 있습니다.
각 파라미터는 ,로 구분됩니다.

Default arguments

함수의 파라미터는 디폴트 인자를 가질 수 있습니다. 이러한 디폴트 인자는 함수를 사용 할 때, 생략해서 사용할 수 있습니다.
디폴트 인자는 함수의 타입 뒤에 =을 붙여서 사용합니다.

fun printNum(numb: Int = 0) {
	println(numb)
}

fun main() {
	printNum() // numb = 0
   printNum(12) // numb = 12
}

오버라이딩 된 함수에서 디폴트 인자를 바꿀 수 없습니다. 부모 클래스에서 지정한 디폴트 인자를 그대로 사용합니다.

 open class A {
    open fun foo(i: Int = 10) { /*...*/ }
}

class B : A() {
    override fun foo(i: Int) { /*...*/ }  // No default value is allowed.
}

class C : A() {
	override fun foo(i: Int = 3) { /*...*/ }  // <- Error
}

만약 디폴트 인자가 그렇지 않은 인자 앞에 온다면, 이름 붙인 인자를 사용해야만 디폴트 인자를 사용할 수 있습니다.

fun numberPrint(num1: Int = 0, num2: Int) {
	println(num1, num2)
}

fun main() {
	numberPrint(num2 = 3) // output: 03
    numberprint(3) // Error
}

만약 디폴트 인자 뒤에 나오는 마지막 인자가 람다일 경우, 이름 붙인 인자를 사용하거나 괄호 밖에서 인자를 넘길 수 있습니다.

fun printNum(num1: Int = 0, num2: Int = 1, num3: () -> Unit) {
	println("$num1, $num2, ")
	num3()
}

fun main() {
	printNum(num3 = { println(2) })
    printNum { println(2) }
}

Named arguments

함수를 호출할 때 하나 이상의 인수에 이름을 지정할 수 있습니다. 이는 함수에 인수가 많고 값을 인수와 연결하기 어려운 경우, 특히 부울 또는 null 값인 경우에 유용할 수 있습니다.

이름 붙인 인자를 사용할 때, 인자의 순서를 자유롭게 바꿀 수 있습니다.

fun reformat(
    str: String,
    normalizeCase: Boolean = true,
    upperCaseFirstLetter: Boolean = true,
    divideByCamelHumps: Boolean = false,
    wordSeparator: Char = ' ',
) { /*...*/ }

reforamt("hello", false, false, true, '.')
reformat("hello")
reformat(
	"hello", 
	wordSeperator='.', 
    divideByCamelHumps=true, 
    upperCaseFirstLetter=false,
    normalizeCase=true
    )

JVM: Java 바이트코드가 항상 함수 매개변수의 이름을 보존하지 않기 때문에 Java 함수를 호출할 때 명명된 인수 구문을 사용할 수 없습니다.

Unit-returning functions

만약 함수가 의미있는 값을 반환하지 않는다면, 그 함수의 리턴 타입은 Unit입니다. Unit은 Unit이라는 값이 하나만 있는 유형입니다. 이 값은 명시적으로 반환될 필요가 없습니다.

fun printHello(name: String?): Unit {
    if (name != null)
        println("Hello $name")
    else
        println("Hi there!")
    // `return Unit` or `return` is optional
}

fun printHello(name: String?) { ... } // 함수 선언부의 Unit도 생략 가능

Single-expression functions

함수가 단일 표현식을 반환할 때, 중괄호를 생략하고 = 기호를 사용할 수 있습니다.

fun pow(x: Int): Int {
	return (x * x)
}

fun pow(x: Int) = x * x

컴파일러가 함수의 반환타입을 추론할 수 있다면, 함수의 반환타입을 지정하는 것은 optional입니다.

Explicit return types

block body를 갖는 함수(중괄호가 있는 함수)는 Unit을 반환하는 것이 목적이 아니라면 반드시 명시적으로 반환 타입을 지정해야합니다.

코틀린은 block body를 갖는 함수에 대해 반환 타입을 추론할 수 없습니다. 왜냐하면 그런 함수는 본문에서 다양한 분기가 발생할 가능성이 있기 때문입니다.

Infix notation(중위 표현)

infix가 키워드가 붙은 함수는 중위 표현을 사용하여 호출될 수 있습니다. 중위 함수는 반드시 아래의 조건을 만족해야 합니다.

  • 반드시 멤버함수 또는 확장함수여야 한다.
  • 하나의 파라미터만을 가져야한다.
  • 그 파라미터는 vararg가 아니고 디폴트 인자를 가지면 안된다.
infix fun Int.shl(x: Int): Int { ... }

// 중위 표현을 사용한 함수 호출
1 shl 2

// 일반적인 멤버함수 호출
1.shl(2)

infix 함수 호출은 산술 연산보다 우선순위가 낮습니다.
따라서 1 shl 1 * 2는 1 shl (1*2)와 같습니다.
반면 infix함수 호출은 boolean연산 보다는 우선순위가 높습니다.
따라서 a && b shl 2는 a && (b shl 2)와 같습니다.

Function scope

코틀린에서 함수는 최상위에 선언될 수 있습니다. 즉, 함수를 만들기 위해 클래스를 생성할 필요가 없습니다. 그외에도 함수는 멤버함수나 확장함수처럼 지역적으로 선언될 수도 있습니다.

Local functions

코틀린은 함수 내에 함수를 선언하는 지역함수를 지원합니다.

fun foo() {
    val foo = "foo"
    fun bar() {
        println(foo) // foo의 지역변수에 접근 가능.
    }
    bar()
}

fun main() {
    foo()
}

지역함수는 바깥에 있는 함수의 지역변수에 접근할 수 있습니다.

Member functions

멤버함수는 클래스나 익명객체 내에 존재하는 함수입니다.

class MyClass() {
	fun print() {
    	println("this is my class")
    }
}

멤버함수는 다음과 같이 클래스 뒤에 .과 함수호출을 붙여서 사용합니다.

MyClass().print()

Tail recursive functions

꼬리 재귀 함수를 사용하면 일반적으로 반복문을 사용하는 알고리즘에 대해서 스택 오버플로우의 위험 없이 재귀함수를 사용할 수 있습니다.

함수 앞에 tailrec modifier를 붙이고 특정한 조건을 만족하면 컴파일러는 재귀를 빠르고 효율적인 반복문으로 최적화합니다.

tailrec fun tailrecPrac(n: Int, ret: Int): Int {
    if (n <= 0)
        return ret
    return tailrecPrac(n - 1, ret + n)
}

fun main() {
    val ans = tailrecPrac(10, 0)
    println(ans)
}

tailrecPrac 함수는 최적화 되면 아래와 같이 최적화됩니다.

fun tailrecPrac(n: Int, ret: Int): Int {
	while (n > 0) {
        ret += n
        n = n - 1
	}
    return ret
}

이렇게 꼬리재귀는 일반적인 재귀함수처럼 스택에 함수를 쌓지 않기 때문에 스택 오버플로우가 일어날 위험이 없습니다.

출처

코틀린 공식 문서

profile
수신제가치국평천하

0개의 댓글