함수 선언
코틀린에선 fun 키워드로 선언한다
fun double(x: Int): Int{
return 2
}
함수 사용
전통적인 방식으로 호출 및 멤버 함수는 점 부호로 호출
double(2)
Simple().foo()
파라미터와 인자
파스칼 표기법(name: type)를 사용해서 파라미터를 정의한다. 또한 기본 값을 가질 수 있다.
fun powerOf(number: Int, exponent: Int = 0){}
기본 값이 있는 경우 인자를 넣지 않을 경우 기본값을 사용한다. 이는 오버 로딩 함수의 개수를 줄여준다.
오버라이딩오버 라이딩 메서드는 항상 베이스 메서드와 같은 기본값을 가진다. 또한 기본 값을 갖는 메서드 오버 라이딩 시, 시그니처에서 기본 파라미터 값을 생략해야 한다.
open class A {
open fun foo(i: Int = 10){}
}
open class B : A() {
override fun foo(i: Int){}
}
만일 기본 값을 갖는 파라미터가 기본값이 없는 파라미터 보다 앞에 위치한다면(a: Int = 1, b: Int)
, 이름 가진 인자로 함수를 호출할 때에만 기본값을 사용한다.
fun foo(bar: Int = 0, baz: Int){}
foo(baz = 1) // 이름있는 인자를 섰기 때문에 bar는 0을 사용한다.
하지만 함수 호출 시 마지막 인자를 괄호 밖에 람다로 전달하면, 기본 파라미터에 값을 전달하지 않고 넘기는 것을 허용한다.
fun foo(bar: Int = 0, baz: Int = 1, qux: () -> Unit){}
foo(1){} // baz는 기본값 1 을 사용
foo(){} // 둘다 기본 값 사용
이름 가진 인자(Named Argument)
함수를 호출할 때 이름을 줄 수 있다. 특히 인자가 많거나 데이터 클래스 호출 시 가독성을 높일 수 있어서 좋다.
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, divideByCamelHumps = true)
다만 위치 기반 인자와 이름 가진 인자를 함께 사용할 때, 모든 위치 기반 인자는 이름 가진 인자 앞에 위치해야 한다. 예를 들면, f(1, y = 2)
는 되지만 f(y = 2, 1)
는 허용하지 않는다.
자바는 바이트코드가 함수 파라미터의 이름을 항상 유지하지 않기 때문에 사용할 수 없다.
spread 연산자를 사용하면 가변 인자에 배열을 넘겨줄 수 있다.
fun foo(vararg strings: String){}
foo(strings = *arrayOf("a", "b", "c")) //단일값에는 필요없다.
Unit 리턴 함수
함수 리턴 없으면 Unit리턴 함, 근데 명시적으로 리턴하면 안 된다.
단일 식 함수
함수 내용이 얼마 없을 때 등호를 이용해서 단일 식을 지정할 수 있다.
fun double(x: Int) = x * 2
리턴 타입을 유추할 수 있으면 생략해도 된다.
명시적 리턴 타입
코틀린은 바디를 가진 함수의 리턴 타입을 유추하지 않는다. 복잡한 제어 흐름을 가지면 리턴 타입을 독자가 알 수 없기 때문이다. 따라서 명시적으로 지정해야 한다.
가변 인자
함수 파라미터에 vararg 수식어로 선언한다. 한 개 이상의 인자를 전달할 수 있도록 한다.
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for( t in ts)
result.add(t)
return result
}
val list = asList(1, 2, 3)
T타입의 vararg파라미터는 함수에서 T배열로 접근한다. 그 예로, 위 코드에서 ts변수는 Array <out T> 타입이다.
가변 파라미터 뒤의 파라미터 값은 이름 가진 인자 구문으로 전달할 수 있다. 만일 함수 타입이면 람다를 괄호 밖으로 전달할 수 있다.
spread 연산자로 다음과 같이 전달할 수도 있다.
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)
중위 부호
다음 경우 중위 부호를 사용해서 함수를 호출할 수 있다.
- 멤버 함수 이거나 확장 함수 경우
- 파라미터가 한 개인 경우
- infix 키워드를 붙인 경우
infix fun Int.shl(x: Int): Int{}
//infix 부호를 사용한 확장 함수를 호출
1 shl 2
//다음과 같다.
1.shl(2)
함수 범위
코틀린은 함수를 최상위 수준으로 선언할 수 있다. 최상위 수준뿐만 아니라 로컬, 멤버, 확장 함수로 선언할 수 있다.
로컬 함수
fun dfs(g: Graph){
val visited = HashSet<Vertex>() //local
fun dfs(current: Vertex){
if(!visited.add(current)) return
for (v in current.neighbors)
dfs(v, visited)
}
dfs(graph.vertices[0], HashSet())
}
로컬 함수는 외부 함수의 로컬 변수에 접근할 수 있다.
멤버 함수
클래스나 오브젝트에 선언된 함수이다.
class A {
fun b() {}
}
A().b()
점 부호를 통해 호출할 수 있다.
제네릭 함수
홑 화살 괄호를 통해 선언할 수 있다.
fun <T> singletonList(item: T): List<T>{
}
자세한 내용은 제네릭을 참고
인라인 함수
[참고]
확장 함수
참고
고차 함수와 람다
참고
꼬리 재귀 함수
코틀린은 꼬리 재귀로 알려진 함수형 프로그래밍 방식을 지원한다. 스택 오버플로 위험 없이 재귀 함수로 알고리즘을 작성할 수 있도록 한다.
함수에 tailrec
수식어가 있고 요구사항을 충족하면, 컴파일러는 재귀를 최적화해서 빠르고 효율적인로푸 기반 버전으로 바꾼다.
tailrec fun findFixPoint(x: Double = 1.0): Double = if( x == Math.cos(x)) x else findFixPoint(Math.cos(x))
//다음과 같다.
private fun findFixPoint(): Double{
var x = 1.0
while (true){
val y = Math.cos(x)
if( x == y ) return y
x = y
}
}
꼬리 재귀 함수가 가능하려면 마지막 연산으로 자기 자신을 호출해야 한다. 재귀 호출 이후 다른 함수가 있으면 사용할 수 없다.
try/catch/finally 블록 안에선 꼬리 재귀 함수를 사용할 수 없다. 꼬리 재귀 함수는 JVM 벡엔드에서만 지원한다.
'코틀린' 카테고리의 다른 글
16. 코루틴 기본 (0) | 2022.06.13 |
---|---|
15. 인라인 함수 (0) | 2022.06.13 |
14. 고차함수와 람다 (0) | 2022.06.12 |
12. 위임 (0) | 2022.06.12 |
11. 오브젝트 식과 선언 (0) | 2022.06.12 |