Functions
함수 선언
Kotlin 함수는 fun 키워드를 사용해서 선언
fun double(x: Int): Int {
}
함수 사용방법
함수 호출은 전통적인 접근식으로 사용
val result = double(2)
멤버 함수 호할 때는 점 부호를 사용
Sample().foo() // Sample 클래스의 인스턴스를 생성하고 foo 호출
중위 표기법 (Infix notation)
함수는 중위 표기법을 사용하여 호출할 수 있다.
- 멤버 함수 또는 확장 함수일 때
- 단일 매개 변수일 때
infix
키워드로 표기로 했을 때
// Int의 확장 함수 선언
infix fun Int.shl(x: Int): Int {
...
}
// 중위 표기법을 사용해서 확장 함수 호출
1 shl 2
// 다음과 같다
1.shl(2)
파라매터 (Parameters)
함수 파라매터는 파스칼 표기법(name : type)을 사용하여 정의된다. 파라매터는 쉼표로 구분된다. 각 파라매터는 명시적으로 입력해야 한다.
fun powerOf(number: Int, exponent: Int) {
...
}
기본 인자
함수 파라매터는 기본값을 가질 수 있으며 해당 인자가 생략될 때 사용
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size()) {
...
}
기본값은 값과 함께 = after 타입을 사용하여 정의
오버라이딩 메소드는 항상 기본 메소드와 동일한 기본 파라매터 값을 사용한다. 기본 파라매터 값을 사용하여 메서드를 오버라이딩하는 경우 시그니쳐에서 기본 파라매터 값을 생략해야한다.
open class A {
open fun foo(i: Int = 10) { ... }
}
class B : A() {
override fun foo(i: Int) { ... } // 허용된 기본값이 없
}
이름 인자 (Named Arguments)
함수 파라매터는 함수를 호출할 때 이름을 지정할 수 있다.
주어진 함수
fun reformat(str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ') {
...
}
기본 인자를 사용하여 호출 할 수 있다.
reformat(str)
그러나 기본값이 아닌 호출을하면 호출은 다음과 같다.
reformat(str, true, true, false, '_')
이름 인자를 사용하면 코드를 훨씬 쉽게 읽을 수 있다.
reformat(str,
normalizeCase = true,
upperCaseFirstLetter = true,
divideByCamelHumps = false,
wordSeparator = '_'
)
모든 인자가 필요하지 않으면 더 간결해진다
reformat(str, wordSeparator = '_')
Java 바이트 코드는 항상 함수 파라매터의 이름을 유지하지 않기 때문에 Java 함수를 호출 할 때는 이름 인자 구문을 사용할 수 없다.
Unit 리턴 함수 (Unit-returning functions)
함수가 유용한 값을 반환하지 않으면 반환 값의 타입은 Unit
이다. Unit
타입의 값은 Unit
한 개만 존재한다. 이 값을 명시적으로 반환할 필요는 없다.
fun printHello(name: String?): Unit {
if (name != null)
println("Hello ${name}")
else
println("Hi there!")
// `return Unit` 또는 `return` 생략 가능
}
Unit
반환 타입 선언은 선택 사항이다. 위 코드는 다음과 같다.
fun printHello(name: String?) {
...
}
단일 표현식 함수 (Single-Expression functions)
함수가 단일 표현식을 리턴하면 중괄호를 생략하고 = 기호 다음에 본문을 지정할 수 있다.
fun double(x: Int): Int = x * 2
컴파일러에서 이를 유추 할 수있는 경우에는 명시적인 리턴 타입 선언은 선택 사항이다.
fun double(x: Int) = x * 2
명시적인 리턴 타입
블록 본문이 있는 함수는 Unit
을 리턴하지 않는 한 리턴 타입을 명시적으로 지정해야한다 (이 경우 선택이다). Kotlin은 블록이 있는 함수는 본문에서 복잡한 제어 흐름을 가질 수 있으며 리턴 타입은 읽는 사람(그리고 때로는 컴파일러의 경우조차도)에게 명확하지 않기 때문에 리턴 타입을 추론하지 않는다.
가변 인자 (Varargs)
함수의 파라매터 (일반적으로 마지막 파라매터)는 varars
제한자로 지정할 수 있다.
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts 는 Array
result.add(t)
return result
}
매개변수 개수를 함수에 전달할 수 있다.
val list = asList(1, 2, 3)
함수 내에서 T 타입의 vararg
파라매터는 T
의 배열로 표시된다. 즉, 위의 예에서 ts
변수는 Array<out T>
타입이다.
하나의 파라매터만 vararg
로 표시될 수 있다. vararg
파라매터가 목록의 마지막 파라매터가 아닌 경우, 이름 인자 구문을 사용하여 전달할 수 있다. 파라매터에 함수 타입이면 괄호 안에 람다를 전달할 수 있다.
vararg
-함수를 호출 할 때 인자를 하나씩 전달할 수 있다. asList(1, 2, 3)
또는 이미 배열이 있고 그 내용을 함수에 전달하려는 경우 spread 연산자를 사용한다 (배열에 *
접두사 사용).
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)
함수 범위 (Function Scope)
Kotlin은 파일의 최상위 레벨에서 함수를 선언 할 수 있다. 즉, Java, C# 또는 Scala와 같은 언어처럼 함수를 가지는 클래스를 만들 필요가 없다. 최상위 함수 외에도 Kotlin은 멤버 함수 및 확장 함수로 로컬로 선언할 수도 있다.
로컬 함수 (Local Functions)
Kotlin은 로컬 함수, 즉 다른 함수 내부의 함수를 지원다.
fun dfs(graph: Graph) {
fun dfs(current: Vertex, visited: Set<Vertex>) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v, visited)
}
dfs(graph.vertices[0], HashSet())
}
로컬 함수는 외부 함수의 로컬 변수 (즉, 클로저)에 액세스 할 수 있으므로 위의 경우 visited 는 로컬 변수가 될 수 있다.
fun dfs(graph: Graph) {
val visited = HashSet<Vertex>()
fun dfs(current: Vertex) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(graph.vertices[0])
}
멤버 함수 (Member Functions)
멤버 함수는 클래스 또는 객체 내부에 정의된 함수이다
class Sample() {
fun foo() { print("Foo") }
}
멤버 함수는 점 표기법으로 호출된다.
Sample().foo() // Sample 클래스의 인스턴스를 생성하고 foo를 호출한다.
제네릭 함수 (Generic Functions)
함수는 함수 이름 앞에 꺾쇠 괄호를 사용하여 제네릭 파라매터를 가질 수 있다.
fun <T> singletonList(item: T): List<T> {
// ...
}
일반 함수에 대한 자세한 내용은 Generics를 참조
인라인 함수 (Inline Functions)
인라인 함수는 여기에서 설명한다
확장 함수 (Extension Functions)
확장 함수는 자체 섹션에서 설명한다
고차 함수와 람다
고차 함수와 람다는 자체 섹션에서 설명한다.
Tail 재귀 함수 (Tail recursive functions)
Kotlin은 꼬리 재귀라는 함수 프로그래밍 스타일을 지원한다. 재귀 함수를 사용하여 루프를 사용하여 작성되지만 스택 오버플로의 위험이없는 알고리즘을 작성할 수 있다. 함수에 tailrec
제한자로 표기하고 필수 요구를 충족하면 컴파일러는 재귀를 최적화하여, 빠르고 효과적으로 루프 기반 버전으로 바꾼다.
tailrec fun findFixPoint(x: Double = 1.0): Double
= if (x == Math.cos(x)) x else findFixPoint(Math.cos(x))
이 코드는 수학의 상수인 코사인 고정 소수점을 계산한다. 1.0에서 시작하여 결과가 더 이상 변하지 않을 때까지 Math.cos를 반복적으로 호출한다. 결과는 0.7390851332151607이다. 결과 코드는이 전통적인 스타일과 동일하다.
private fun findFixPoint(): Double {
var x = 1.0
while (true) {
val y = Math.cos(x)
if (x == y) return y
x = y
}
}
tailrec
제한자를 사용하려면 함수가 수행하는 마지막 작업으로 함수를 호출해야한다. 재귀 호출 후에 더 많은 코드가있을 때 꼬리 재귀를 사용할 수 없으며 try/catch/finally 블록 내에서 사용할 수 없다. 현재 꼬리 재귀는 JVM 백엔드에서만 지원된다.