[Kotlin] 4-1. 함수

함수와 람다

함수

함수 선언

코틀린에서 함수는 fun키워드 사용해서 선언

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

함수 사용 전통적인 방식으로 함수 호출

val result = double(2)

멤버 함수 호출은 점 부호를 사용

Sample().foo()  // Sample클래스의 인스턴스를 생성하고 foo를 호출

파라미터

파라미터 표기법(name: type)을 사용해서 파라미터 정의 / 각 파라미터 콤마로 구분

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

기본 인자

함수 파라미터는 기본 값을 가질 수 있다. 이 경우 대응하는 인자를 생략하면 기본 값을 사용한다
이는 다른 언어와 비교해 오버로딩 함수의 개수를 줄여준다

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

타입 뒤에 등호(=)와 값을 사용해서 기본 값을 정의

오버라이딩 메서드는 항상 베이스 메서드와 같은 기본 파라미터 값을 사용한다. 기본 파라미터 값을 갖는
메서드를 오버라이딩 할 때, 시그니처에 기본 파라미터를 값을 생략해야 한다

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

class B : A() {
  override fun foo(i: 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) { println("hello") }  // 기본 값 baz = 1 사용
foo { println("hello") }  // 기본 값 bar = 0, baz = 1 사용

Unit 리턴 함수

함수가 어떤 유용한 값도 리턴하지 않으면, 리턴 타입으로 Unit을 사용
Unit은 Unit을 유일한 값으로 갖는 타입. 이 값을 명시적으로 리턴하면 안 된다

fun printHello(name: String?): Unit {
  if(name != null)
    println("hello ${name}")
  else
    println("hi")
  // 'return Unit' 또는 'return' 생략
}

리턴 타입으로 Unit을 선언하는 것 또한 생략 가능. 위 코드는 다음과 동일

fun printHello(name: String?) {
  ...
}

단일 식 함수

함수가 단일 식을 리턴할 때 중괄호를 생략하고 등호(=) 뒤에 몸체로 단일 식을 지정할 수 있다

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

컴파일러가 리턴 타입을 유추할 수 있으면 리턴 타입을 명시적으로 선언하는 것을 생략할 수 있다

fun double(x: Int) = x*2

명시적 리턴 타입

블록 몸체를 갖는 함수는 Unit 을 리턴한다는 것을 의도하지 않는 이상 항상 리턴 타입을 명시적으로 지정해야 한다. Unit을 리턴하는 경우 생략 가능하다 . 코틀린은 블 록 몸체를 가진 함수의 리턴 타입을 유추할 수 없다. 왜냐면 그런 함수가 복잡한 제어 흐름을 가지면 리턴 타입을 독자가 (그리고 때때로 컴파일러 조차도) 알 수 없기 때문이다.

함수 범위

코틀린은 함수를 파일에서 최상위 수준으로 선언할 수 있다. 이는 자바, C#, 스칼라와 달리 함수를 포함할 클래스를 만들 필요가 없다는 것을 의미한다. 최상위 수준 함수뿐 만 아니라 함수를 로컬로, 멤버 함수로, 확장 함수로 선언할 수 있다.

로컬 함수

코틀린은 다른 함수 안에 선언한 로컬 함수를 지원

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])
}

멤버 함수

멤버 함수는 클래스나 오브젝트에 선언된 함수이다

class Sample() {
  fun foo() { println("Foo") }
}

점 부호를 사용해서 멤버 함수를 호출한다

Sample().foo()  // Sample클래스의 인스턴스를 생성하고 foo를 호출