고차 함수
함수 타입
-
함수 타입은 함수처럼 쓰일 수 있는 값들을 표시하는 타입이다. 문법적으로 이런 타입은 함수 시그니쳐와 비슷하다.
- 괄호로 둘러싸인 파라미터 타입 목록은 함숫 값에 전달될 데이터의 종류와 수를 정의한다.
- 반환 타입은 함수 타입의 함숫 값을 호출하면 돌려 받게 되는 값의 타입을 정의한다.( 반환 값이 없다면 Unit을 반환 타입으로 사용한다.)
- java는 SAM(SingleAbstractMethod)를 문맥에 따라 적절히 함수 타입처럼 취급한다. 그래서 람다, 메소드 참조로 SAM 인터페이스를 인스턴스화 할 수 있다.
- kotlin은
(p1...) -> R은 함수 타입이기에 SAM 인터페이스를 암시적으로 변환할 수 없다. (1.4부터는 가능하다.)
람다와 익명함수
람다
fun aggregate( numbers: Array<Int>, op: (Int, Int) -> Int ):Int {
return 0
}
aggregate(numbers, {a, b -> a + b})
aggregate(numbers) {a, b -> a + b} //trailingClosure 같다.
- 이런 식(
{})이 람다다. - 람다의 파라미터 목록은 괄호로 둘러싸지 않는다. 괄호로 감싸면 구조분해 할당
(destructuring)이 된다. - 람다에 인자가 없으면
->도 생략할 수 있다. - 인자가 하나 밖에 없는 람다는 미리 정해진
it라는 이름을 사용해서 가리킬 수 있다. - 마지막으로 파라미터에서 사용하지 않는 목록은
_으로 지정하면 된다.
익명함수
fun aggregate( numbers: Array<Int>, op: (Int, Int) -> Int ):Int {
return 0
}
aggregate(numbers, fun(result, op): Int { return result + op })
fun() =의 식으로 익명함수를 사용할 수 있다.fun키워드를 사용해서 표현한다.- 파라미터 타입을 추론할 수 있다면 파라미터 타입을 지정하지 않아도 된다.
- 함수 정의와 달리 익명함수이기에 인자로 함수에 넘기거나 변수에 대입하는 등 일반 값처럼 사용할 수 있다.
호출 가능 참조
- 함수 정의가 있고 이 함수 정의를 함수 값처럼 고차 함수에 넘기려면
- 람다로 감싸면 된다.
- 호출 가능 참조(callable reference)로 사용하면 된다. ```kotlin fun check( s: String, condition: (Char) -> Boolean ):Boolean { return true } fun isLowerCase(c: Char) = c.isLowerCase() fun main() { println(check(“Hello”, {c: Char -> c.isLowerCase()})) //lambda println(check(“Hello”){c: Char -> c.isLowerCase()}) //lambda (like trailingClosure) println(check(“Hello”, ::isLowerCase)) //callableReference
} ```
::를 클래스 앞에 하면 생성자 호출 가능 참조를 얻는다.- 바인딩된 호출 가능 참조(bound callable reference)는 주어진 클래스 인스턴스 문맥 안에서 멤버 함수를 호출하고 싶을 때 사용할 수 있다.
- 코틀린 프로퍼티에 대한 호출 가능 참조를 만들 수 있다. 실제로 함숫 값은 아니고, reflection이다.
인라인 함수 프로퍼티
- 고차함수, 함수 값을 사용하면 함수가 객체로 표현되기 때문에 성능적으로 비용이 발생한다.
- 익명함수, 람다가 외부 영역을 사용하면 캡쳐링이 필요하다.
- kotlin은
inline으로 이를 해결한다. inline을 붙이면 객체를 생성하거나 하지 않고 컴파일 타임에 그냥 함수 본문을 그 곳에 넣어버린다.
비지역적 제어흐름
- 고차함수를 사용하면 일부러 제어 흐름을 깰 때 문제가 된다.