Home 코틀린 기본 문법 (함수)
Post
Cancel

코틀린 기본 문법 (함수)

fun

1
2
3
fun double(x: Int): Int{  
    return 2 * x  
}

함수 정의는 fun 키워드를 사용한다.


함수 사용

1
val result = double(2)

함수 호출

1
Stream().read()

함수 파라미터는 Pascal notation 을 써서 정의한다.

각 파라미터는 type 이 정의되어야 한다.

1
fun powerOf(number: Int, exponent: Int) { /*...*/ }

파라미터

함수 파라미터는 argument 가 생략 된 경우에 할당되는
해당 파라미터의 기본 값을 가질 수 있다.

1
fun  read(b:  Array<Byte>, off:  Int  =  0, len:  Int  = b.size) { /*...*/ }  

본 값은 type 과 값 사이에 = 를 사용하여 정의한다.

overriding method 는 항상 base method 와 같은 기본 파라미터 값을 쓴다.

기본 파라미터 값을 가진 method 를 overriding 할 때는 기본 값 할당 표현을 생략할 수 있다.

1
2
3
4
5
6
7
open class A {  
    open fun foo(i: Int = 10) { /*...*/ }   
}  
  
class B : A() {   
    override fun foo(i: Int) { /*...*/ }  
}

함수의 인자 항목에서 기본 값이 있는 파라미터가 기본 값이 없는 파라미터 보다 앞에 위치하면, 기본 값은 named arguments (아래에서 설명) 로 함수를 호출할 때만 쓰이게 된다.

1
2
3
fun foo(bar: Int = 0, baz: Int) { /*...*/ }   
  
foo(baz = 1) // The default value bar = 0 is used

함수의 인자 항목에서 기본값을 가진 파라미터 뒤에 위치한 마지막 인자가 lambda 인 경우, named argument 나 outside the parentheses 로 전달 될 수 있다.

1
2
3
4
5
6
7
fun foo(bar: Int = 0, baz: Int = 1, qux: () -> Unit) { /*...*/ }  

foo(1) { println("hello") } // Uses the default value baz = 1  

foo(qux = { println("hello") }) // Uses both default values bar = 0 and baz = 1  

foo { println("hello") } // Uses both default values bar = 0 and baz = 1

함수 호출 시 파라미터의 이름을 사용할 수 있다.

1
2
3
4
5
6
7
fun reformat(str: String,   
             normalizeCase: Boolean = true,  
             upperCaseFirstLetter: Boolean = true,   
             divideByCamelHumps: Boolean = false,  
             wordSeparator: Char = ' ') {  
/*...*/  
}

기본 argument와 사용할 때

1
reformat(str)

기본 값을 사용하지 않을 때

1
reformat(str, true, true, false, '_')

보기 좋게

1
2
3
4
reformat(str, normalizeCase = true,   
        upperCaseFirstLetter = true,  
        divideByCamelHumps = false,   
        wordSeparator = '_' )

일부 argument만 지정할 때

1
reformat(str, wordSeparator="_")

함수가 positional and named arguments 와 함게 호출되면,

모든 positional arguments 는 첫번째 named argument 앞에 위치헤야 한다.

예를들어 f(1, y = 2) 는 맞지만, f(x = 1, 2) 은 틀리게 된다.

Variable number of arguments (vararg, 아래에서 설명은 spread operator 를 사용하여 named 형식으로 전달될 수 있다.

1
2
fun  foo(vararg  strings:  String) { /*...*/ } 
foo(strings = *arrayOf("a", "b", "c"))  

Single-expression functions

함수가 single expression 을 리턴할 때에는 { } 는 생략될 수 있고 함수의 바디는 = 뒤에 위치한다

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

컴파일러가 expression에서 리턴 타입을 유추 할 수 있는 경우에는 return type 생략 가능

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

함수의 block body가 있는 경우에는 항상 return type을 명시해야하며,

그렇지 않으면 Unit을 리턴하는 것으로 간주한다.

코틀린에서 block body가 있는 함수는 return type을 유추하지 않는데,

이는 함수의 코드가 복잡하거나 return type을 명확히 판단할 수 없기 때문이다.


Variable number of arguments (Varargs)

함수의 파라미터 마지막에는 vararg로 표시될 수 있는데, 여러개의 argument를 함수에 전달 할 수 있게 해준다.

1
2
3
4
5
6
7
8
fun <T> asList(vararg ts: T): List<T> {  
    val result = ArrayList<T>()   
    for (t in ts) // ts is an Array   
 result.add(t)  
    return result    
}  
  
val list = asList(1, 2, 3)

varargs 함수를 호출할 때 asList(1,2,3) 처럼 argument를 하나씩 전달하거나,

이미 array가 있고 그 내용을 함수에 전달하고 싶을때는 spread operator *를

array의 prefix로 사용할 수 있다.

1
2
val a = arrayOf(1, 2, 3)  
val list = asList(-1, 0, *a, 4)

Infix notation

infix 키워드를 가진 함수는 .( ) 를 생략하고 호출하는 infix notation 을 사용하여 호출될 수 있다.

infix function 은 아래의 사항을 만족해야 한다.

  • 멤버 함수 이거나 확장 함수 이어야 한다.
  • 단 하나의 파라미터를 가진다.
  • 파라미터는 고정되지 않은 개수의 arugment 를 가질 수 없고 기본 값을 가질 수 없다.
1
2
3
4
5
6
7
infix fun Int.shl(x: Int): Int { ... }  
  
// calling the function using the infix notation  
1 shl 2  
  
// is the same as  
1.shl(2)

infix function 호출은 arithmetic operators, type casts, and the rangeTo operator 보다 우선순위가 낮다.

아래의 예제들은 모두 동등하다.

  • 1 shl 2 + 3 is equivalent to 1 shl (2 + 3)
  • 0 until n * 2 is equivalent to 0 until (n * 2)
  • xs union ys as Set<*> is equivalent to xs union (ys as Set<*>)
boolean operators && and ** , **is- and in-checks, and some other operators 보다는 우선 순위가 높다.

아래의 예제들은 모두 동등하다.

  • a && b xor c is equivalent to a && (b xor c)
  • a xor b in c is equivalent to (a xor b) in c

Local functions

코틀린은 함수 안의 함수를 지원한다.

1
2
3
4
5
6
7
8
9
fun dfs(graph: Graph) {   
    fun dfs(current: Vertex, visited: MutableSet<Vertex>) {  
        if (!visited.add(current)) return   
for (v in current.neighbors)   
            dfs(v, visited)   
    }   
      
    dfs(graph.vertices[0], HashSet())  
}

로컬함수는 외부 함수의 변수에 접근할 수 있다.

1
2
3
4
5
6
7
8
9
10
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])   
}

멤버 함수

멤버 함수는 클래스나 오브젝트의 내부에 정의된다.

1
2
3
4
5
class Sample(){  
    fun foo(){ print("Foo") }  
}  
  
Sample().foo()

익명 함수

자바에 익명 함수가 있듯이 kotlin에서도 익명 함수를 사용할 수 있다.

1
2
3
val stringLengthFunc: (String) -> Int = { input ->
    input.length
}

제네릭 함수

함수 이름 앞에 <>를 사용하여 지정된 generic parameters를 가질 수 있다.

1
fun  <T>  singletonList(item:  T): List<T> { /*...*/ }  

Tail recursive functions

코틀린은 tail recursion 라고 알려진 함수형 프로그래밍 스타일을 지원한다.

이는 특정 알고리즘의 경우 재귀형 함수 대신 루프를 사용할 수 있고,

stack overflow 의 위험을 피할 수 있게 해준다.

함수가 tailrec 로 표시되고 특정 조건들을 만족할 때 컴파일러는 재귀구문을 효과적인 루프 기반의 버전으로 최적화 한다.

1
2
3
val eps = 1E-10 // "good enough", could be 10^-15   
tailrec fun findFixPoint(x: Double = 1.0): Double   
 = if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))

이 코드는 Math.cos를 1.0에서 시작해서 결과가 더 이상 바뀌지 않을때 까지 반복적으로 호출하여 eps 0.7390851332151611 값을 넘긴다.

이는 좀 더 전통적인 아래의 코드와 동등하다.

1
2
3
4
5
6
7
8
9
10
11
val eps = 1E-10 // "good enough", could be 10^-15  
  
private fun findFixPoint(): Double {   
    var x = 1.0   
      
 while (true) {  
        val y = Math.cos(x)   
        if (Math.abs(x - y) < eps) return x   
        x = Math.cos(x)   
    }   
}

tailrec 에 좀더 맞게 하려면 함수는 자신이 마지막 호출 대상이 되어야 한다.

재귀 호출 이후의 더이상의 코드가 없어야 tail recursion 을 쓸 수 있으며,

try/catch/finally 구문과 함께 쓸 수 없다.

This post is licensed under CC BY 4.0 by the author.