728x90
반응형
메소드를 다른 클래스에 추가: 확장 함수와 확장 프로퍼티
기존 코드와 코틀린 코드를 자연스럽게 통합하는 것은 코틀린의 핵심 목표 중 하나임
이런 기존 자바 API를 재작성하지 않고도 코틀린이 제공하는 여러 편리한 기능을 사용할 수 는 없을까?
바로 확장 함수가 그런 역할을 해줄 수 있음
개념적으로 확장 함수는 단순함
확장 함수는 어떤 클래스의 멤버 메소드인 것처럼 호출할 수 있지만 그 클래스의 밖에 선언된 함수임
문자열의 마지막 문자를 돌려주는 확장 메소드를 추가해보자
package strings
fun String.lastChar(): Char = this.get(this.length - 1)
확장 함수를 만들려면 추가하려는 함수 이름 앞에 그 함수가 확장할 클래스의 이름을 덧붙이기만 하면 됨
클래스 이름을 수신 객체 타입(receiver type)이라 부르며, 확장 함수가 호출되는 대상이 되는 값(객체)을 수신 객체(receiver object)라고 부름
println("Kotlin".lastChar())
// String = 수신 객체 타입
// "Kotlin" = 수신 객체
확장 함수는 클래스 안에서 정의한 메소드와 달리 확장 함수 안에서는 클래스 내부에서만 사용할 수 있는 비공개(private) 멤버나 보호된(protected) 멤버를 사용할 수 없음
임포트와 확장 함수
확장 함수를 사용하기 위해서는 그 함수를 다른 클래스나 함수와 마찬가지로 임포트해야만 함
확장 함수를 임포트 없이 사용한다면 동일한 이름의 확장 함수와 충돌할 수도 있기 때문에 임포트로 어떤 확장함수인지 명시해 주어야 함
import strings.lastChar // 명시적으로 사용
import strings.* // * 사용 가능
import strings.lastChar as last // as 키워드를 사용 가능
자바에서 확장 함수 호출
자바에서 확장 함수를 사용하기도 편함
정적 메소드를 호출하면서 첫 번째 인자로 수신 객체를 넘기기만 하면 됨
다른 최상위 함수와 마찬가지로 확장 함수가 들어있는 자바 클래스 이름도 확장 함수가 들어있는 파일 이름에 따라 결정됨
따라서 확장 함수를 StringUtil.kt 파일에 정의했다면 다음과 같이 호출할 수 있음
char c = StringUtilKt.lastChar("java");
확장 함수로 유틸리티 함수 정의
joinToString 함수의 최종 버전을 만들어보기
이제 이 함수는 코틀린 하이브러리가 제공하는 함수와 거의 같아졌음
fun <T> Collection<T>.joinToString(
separator: String = ", ",
prefix: String = "",
postfix: String = ""
): String {
val result = StringBuilder(prefix)
for ((index, element) in this.withIndex()) {
if (index > 0) result.append(separator)
result.append(element)
}
result.append(postfix)
return result.toString()
}
fun main(args: Array<String>) {
val list = arrayListOf(1, 2, 3)
println(list.joinToString(" "))
}
확장 함수는 단지 정적 메소드 호출에 대한 문법적인 편의일 뿐임
그래서 클래스가 아닌 더 구체적인 타입을 수신 객체 타입으로 지정할 수도 있음
확장 함수는 오버라이드할 수 없음
확장 함수는 클래스의 일부가 아님
확장 함수는 클래스 밖에 선언됨
이름과 파라미터가 완전히 같은 확장 함수를 기반 클래스와 하위 클래스에 대해 정의해도 실제로는 확장 함수를 호출할 때 수신 객체로 지정한 변수의 정적 타입에 의해 어떤 확장함수가 호출될지 결정되지, 그 변수에 저장된 객체의 동적인 타입에 의해 확장 함수가 결정되지 않음
더보기
💡 확장 함수 팁
어떤 클래스를 확장한 함수와 그 클래스의 멤버 함수의 이름과 시그니처가 같다면 확장 함수가 아니라 멤버 함수가 호출됨(멤버 함수의 우선순위가 더 높음). 클래스의 API를 변경할 경우 항상 이를 염두에 둬야 함.
확장 프로퍼티
확장 프로퍼티를 사용하면 기존 클래스 객체에 대한 프로퍼티 형식의 구문으로 사용할 수 있는 API를 추가할 수 있음
프로퍼티라는 이름으로 불리기는 하지만 상태를 저장할 적절한 방법이 없기 때문에 실제로 확장 프로퍼티는 아무 상태도 가질 수 없음
val String.lastChar: Char
get() = get(length -1)
뒷받침하는 필드가 없어서 기본 게터 구현을 제공할 수 없으므로 최소한 게터는 꼭 정의를 해야 함
마찬가지로 초기화 코드에서 계산한 값을 담을 장소가 전혀 없으므로 초기화 코드도 쓸 수 없음
var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value: Char) {
this.setCharAt(length - 1, value)
}
fun main(args: Array<String>) {
println("Kotlin".lastChar)
val sb = StringBuilder("Kotlin?")
sb.lastChar = '!'
println(sb)
}
반응형
'프로그래밍 > Kotlin' 카테고리의 다른 글
코틀린에서 자주 사용하는 문법 String Template (0) | 2022.01.21 |
---|---|
코틀린에서 컬렉션 만들기 (0) | 2022.01.19 |
코틀린의 예외 처리 (0) | 2022.01.12 |
코틀린 기초(while과 for) (0) | 2022.01.11 |
코틀린 기초(enum과 when) (0) | 2022.01.10 |