일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- architecture
- 코드포스
- GitHub
- 프로그래머스
- Coroutines
- androidStudio
- relay
- AWS
- livedata
- TEST
- Compose
- textfield
- MyVoca
- boj
- 코루틴
- Kotlin
- Gradle
- MiTweet
- Python
- 암호학
- Hilt
- 쿠링
- android
- activity
- ProGuard
- 백준
- pandas
- Coroutine
- Rxjava
- Codeforces
- Today
- Total
이동식 저장소
[Kotlin] 함수 타입 상속받기 본문
Kotlin에서는 함수 역시 하나의 타입으로 취급된다. 함수를 변수로 참조할 수 있지 않는가? 심지어 클래스나 인터페이스가 함수를 상속받을 수도 있다.
아무리 그래도 그렇지, 어떻게 함수를 상속받아요
나도 몰랐던 내용인데, 가능하다고 한다. 그 전에 우선 invoke 함수에 대해 알아보자.
invoke
operator 함수를 선언하면 ``+``, ``[]`` 등 여러 연산자를 사용할 수 있다. invoke도 그러한 함수 중 하나로, invoke를 선언하면 객체를 함수처럼 호출할 수 있다.
class Greeter(val greeting: Greeting) {
operator fun invoke(name: String) = println("$greeting, $name!")
}
val me = Greeter("안녕")
me("hsk") // 안녕, hsk!
``Greeter`` 타입의 객체에 괄호를 붙여 함수처럼 호출하였다. ``me("hsk")``는 ``me.invoke("hsk")``와 같다. 필요에 따라 invoke를 오버로딩해도 된다.
이제 invoke가 함수 타입과 어떤 관계인지 알아보자.
invoke와 함수 타입
내부적으로 람다식은 (inline되지 않았다면) invoke 함수만을 가지는 인터페이스를 상속받은 클래스로 컴파일된다. 람다식을 실행한다는 것은 사실 invoke 함수를 호출하는 것과 같다.
예를 들어 2개의 매개변수를 갖는 람다식은 다음 인터페이스를 상속한 클래스로 정의된다.
interface Function2<in P1, in P2, out R> {
operator fun invoke(p1: P1, p2: P2): R
}
람다식이 클래스로 컴파일되므로, 아예 처음부터 클래스로 선언할 수도 있다. 평범한 클래스 함수처럼 선언하면 된다. 람다식이 복잡한 경우 클래스에 여러 함수로 쪼개어 작성할 수 있다.
클래스에서 위의 ``FunctionN``(N은 정수) 인터페이스 또는 ``(P1, P2) -> R`` 타입을 상속받으면 된다. 다음은 함수 타입을 상속받아 ``Issue`` 객체를 필터링하는 클래스이다.
data class Issue(
val id: String, val project: String, val type: String,
val priority: String, val description: String)
}
class ImportantIssuePredicate(val project: String) : (Issue) -> Boolean {
override fun invoke(issue: Issue): Boolean {
return issue.project == project && issue.isImportant()
}
private fun Issue.isImportant(): Boolean {
return type == "Bug" &&
(priority == "Major" || priority == "Critical")
}
}
val issues - listOf(...)
val predicate = ImportantIssuePredicate("IDEA")
issue.filter(predicate).forEach { println(it) }
내부 로직이 람다식으로 작성하기엔 꽤 복잡하다. 그래서 ``(Issue) -> Boolean`` 타입을 상속받는 predicate 클래스 내부에 로직을 쪼개어 작성하였다. 함수 타입을 상속받은 클래스는 타입에 맞는 ``invoke`` 함수를 override해야 한다.
이렇게 하면 람다식의 구현을 숨기면서 같은 역할을 하는 코드를 깔끔하게 작성할 수 있다. 자주 쓰는 테크닉은 아니지만, 잘 기억해 두자.
'Primary > Kotlin' 카테고리의 다른 글
[Kotlin] value class로 값을 감싸보자 (0) | 2022.06.17 |
---|---|
[Kotlin] 클래스 안에 확장 함수? (0) | 2022.06.16 |
[Kotlin] 제네릭과 타입 간의 관계 (0) | 2022.06.13 |
[Kotlin] 런타임에서의 제네릭과 reified (0) | 2022.06.12 |
[Kotlin] 제네릭 타입 제한하기 (0) | 2022.06.10 |