코틀린에서 함수는 fun 키워드를 사용하여 선언할 수 있습니다.
fun helloWorld() {
println("Hello World")
}
fun printString(str: String) {
println(str)
}
함수의 파라미터는 (변수명: 타입)의 형태로 작성할 수 있습니다.
각 파라미터는 ,로 구분됩니다.
함수의 파라미터는 디폴트 인자를 가질 수 있습니다. 이러한 디폴트 인자는 함수를 사용 할 때, 생략해서 사용할 수 있습니다.
디폴트 인자는 함수의 타입 뒤에 =을 붙여서 사용합니다.
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) }
}
함수를 호출할 때 하나 이상의 인수에 이름을 지정할 수 있습니다. 이는 함수에 인수가 많고 값을 인수와 연결하기 어려운 경우, 특히 부울 또는 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입니다. 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도 생략 가능
함수가 단일 표현식을 반환할 때, 중괄호를 생략하고 = 기호를 사용할 수 있습니다.
fun pow(x: Int): Int {
return (x * x)
}
fun pow(x: Int) = x * x
컴파일러가 함수의 반환타입을 추론할 수 있다면, 함수의 반환타입을 지정하는 것은 optional입니다.
block body를 갖는 함수(중괄호가 있는 함수)는 Unit을 반환하는 것이 목적이 아니라면 반드시 명시적으로 반환 타입을 지정해야합니다.
코틀린은 block body를 갖는 함수에 대해 반환 타입을 추론할 수 없습니다. 왜냐하면 그런 함수는 본문에서 다양한 분기가 발생할 가능성이 있기 때문입니다.
infix가 키워드가 붙은 함수는 중위 표현을 사용하여 호출될 수 있습니다. 중위 함수는 반드시 아래의 조건을 만족해야 합니다.
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)와 같습니다.
코틀린에서 함수는 최상위에 선언될 수 있습니다. 즉, 함수를 만들기 위해 클래스를 생성할 필요가 없습니다. 그외에도 함수는 멤버함수나 확장함수처럼 지역적으로 선언될 수도 있습니다.
코틀린은 함수 내에 함수를 선언하는 지역함수를 지원합니다.
fun foo() {
val foo = "foo"
fun bar() {
println(foo) // foo의 지역변수에 접근 가능.
}
bar()
}
fun main() {
foo()
}
지역함수는 바깥에 있는 함수의 지역변수에 접근할 수 있습니다.
멤버함수는 클래스나 익명객체 내에 존재하는 함수입니다.
class MyClass() {
fun print() {
println("this is my class")
}
}
멤버함수는 다음과 같이 클래스 뒤에 .과 함수호출을 붙여서 사용합니다.
MyClass().print()
꼬리 재귀 함수를 사용하면 일반적으로 반복문을 사용하는 알고리즘에 대해서 스택 오버플로우의 위험 없이 재귀함수를 사용할 수 있습니다.
함수 앞에 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
}
이렇게 꼬리재귀는 일반적인 재귀함수처럼 스택에 함수를 쌓지 않기 때문에 스택 오버플로우가 일어날 위험이 없습니다.