Function types
코틀린은 함수를 다루는 선언에 (Int) -> String
과 같은 함수 타입을 사용한다.
val onClick: () -> Unit = ...
이러한 타입은 함수의 시그니처(매개변수와 반환 값)에 해당하는 특별한 표기법을 가진다.
- 모든 함수 타입은 괄호로 묶인 매개변수 타입 목록과 반환 타입을 가진다:
(A, B) -> C
는 타입A
와B
의 두 인수를 받아 타입C
의 값을 반환하는 함수를 나타내는 타입이다. 매개변수 타입 목록은() -> A
와 같이 비어 있을 수 있습니다.Unit
반환 타입은 생략할 수 없다. - 함수 타입은 선택적으로 추가 수신자 타입을 가질 수 있으며, 이는 표기법에서 점(
.
) 앞에 지정된다:A.(B) -> C
타입은 객체A
에서 매개변수B
와 함께 호출할 수 있고 값C
를 반환하는 함수를 나타낸다. - 일시 중단 함수는 표기법에 suspend 수정자가 있는 특별한 종류의 함수 타입에 속한다.(예:
suspend () -> Unit
또는suspend A.(B) -> C
).
함수 타입 표기법은 선택적으로 함수 매개변수의 이름을 포함할 수 있다: (x: Int, y: Int) -> Point
. 이러한 이름은 매개변수의 의미를 문서화하는 데 사용할 수 있다.
함수 타입이 널 가능함을 지정하려면 다음과 같이 괄호를 사용한다: ((Int, Int) -> Int)?
함수 타입은 괄호를 사용하여 결합할 수도 있다: (Int) -> ((Int) -> Unit)
화살표 표기법은 오른쪽 결합이므로, (Int) -> (Int) -> Unit은 이전 예제와 동일하지만 ((Int) -> (Int)) -> Unit과는 다르다.
타입 별칭을 사용하여 함수 타입에 대체 이름을 지정할 수도 있다:
typealias ClickHandler = (Button, ClickEvent) -> Unit
Instantiating a function type
함수 타입의 인스턴스를 얻는 몇 가지 방법이 있다:
- 함수 리터럴 내에서 코드 블록을 사용하는 방법:
- 람다 표현식:
{ a, b -> a + b }
, - 익명 함수:
fun(s: String): Int { return s.toIntOrNull() ?: 0 }
- 이때 수신자가 있는 함수 리터럴은 수신자가 있는 함수 타입의 값으로 사용할 수 있다.
- 람다 표현식:
- 기존 선언에 대한 호출 가능한 참조 사용:
- 최상위, 로컬, 멤버 또는 확장 함수:
::isOdd
,String::toInt
, - 최상위, 멤버 또는 확장 속성:
List<Int>::size
, - 생성자:
::Regex
- 여기에는 특정 인스턴스의 멤버를 가리키는 바운드 호출 가능 참조가 포함된다:
foo::toString
.
- 최상위, 로컬, 멤버 또는 확장 함수:
- 인터페이스로 함수 타입을 구현하는 사용자 정의 클래스의 인스턴스 사용:
class IntTransformer: (Int) -> Int { override operator fun invoke(x: Int): Int = TODO() }
val intFunction: (Int) -> Int = IntTransformer()
컴파일러는 충분한 정보가 있는 경우 변수에 대한 함수 타입을 추론할 수 있다.
val a = { i: Int -> i + 1 } // 추론된 타입은 (Int) -> Int
수신자가 있거나 없는 함수 타입의 비 리터럴 값은 서로 교환 가능하므로, 수신자는 첫 번째 매개변수 대신 사용될 수 있으며, 그 반대도 마찬가지다.
예를 들어, (A, B) -> C
타입의 값은 A.(B) -> C
타입의 값이 예상되는 곳에 전달하거나 할당할 수 있으며, 그 반대도 가능하다
val repeatFun: String.(Int) -> String = { times -> this.repeat(times) }
val twoParameters: (String, Int) -> String = repeatFun // 가능
// { times -> this.repeat(times) } == { s: String, i: Int -> s.repeat(i) }
fun runTransformation(f: (String, Int) -> String): String {
return f("hello", 3)
}
val result = runTransformation(repeatFun) // 가능
Invoking a function type instance
함수 타입의 값은 invoke(...)
연산자를 사용하여 호출할 수 있다: f.invoke(x)
또는 간단히 f(x)
값에 수신자 타입이 있는 경우, 수신자 객체는 첫 번째 인수로 전달되어야 한다. 수신자가 있는 함수 타입의 값을 호출하는 또 다른 방법은 마치 그 값이 확장 함수인 것처럼 수신자 객체를 앞에 추가하는 것이다: 1.foo(2)
val stringPlus: (String, String) -> String = String::plus
val intPlus: Int.(Int) -> Int = Int::plus
println(stringPlus.invoke("<-", "->"))
println(stringPlus("Hello, ", "world!"))
println(intPlus.invoke(1, 1))
println(intPlus(1, 2))
println(2.intPlus(3)) // 확장 함수처럼 호출
Function literals with receiver
수신 객체를 갖는 함수 타입(A.(B) -> C
)은 수신 객체가 있는 함수 리터럴이라는 특별한 형태의 함수 리터럴로 인스턴스화할 수 있다.
컴퓨터 과학에서 리터럴은 소스 코드에서 고정 값을 나타내는 표기법이다.
Kotlin은 수신 객체를 제공하면서 수신 객체가 있는 함수 타입의 인스턴스를 호출할 수 있는 기능을 제공한다.
함수 리터럴의 본문 내부에서는, 호출 시 전달된 수신 객체가 암시적인 this
가 되어, 그 수신 객체의 멤버를 별도의 수식자 없이 접근할 수 있게 해 줍니다. 또는 this
표현식을 사용하여 수신 객체에 접근할 수도 있다.
이 동작은 확장 함수의 동작과 유사하다. 확장 함수도 함수 본문 내에서 수신 객체의 멤버에 접근할 수 있도록 해주기 때문이다.
다음은 수신 객체가 있는 함수 리터럴의 타입과 함께 예시를 보여주는 코드입니다. 여기서 plus
는 수신 객체에서 호출된다:
val sum: Int.(Int) -> Int = { other -> plus(other) }
익명 함수 문법은 함수 리터럴의 수신 객체 타입을 직접 지정할 수 있도록 해준다. 이는 수신 객체가 있는 함수 타입의 변수를 선언하고 나중에 사용하려 할 때 유용할 수 있다:
val sum = fun Int.(other: Int): Int = this + other
람다 표현식은 문맥에서 수신자 유형을 유추할 수 있는 경우 수신자와 함께 함수 리터럴로 사용할 수 있다.
class HTML {
fun body() { ... }
}
fun html(init: HTML.() -> Unit): HTML {
val html = HTML() // create the receiver object
html.init() // pass the receiver object to the lambda
return html
}
html { // lambda with receiver begins here
body() // calling a method on the receiver object
}
- 코틀린 공식 문서
- 코틀린
receiver
에 관한 논의
'자바' 카테고리의 다른 글
코틀린 receiver 이해를 위한 예시 정리 (0) | 2025.04.03 |
---|---|
Job (0) | 2025.04.01 |
SupervisorJob (0) | 2025.03.26 |
CoroutineScope (0) | 2025.03.25 |
스레드 비용 (1) | 2025.03.12 |