코틀린 (Kotlin) 확장 함수와 확장 프로퍼티

코틀린 확장 함수

코틀린는 기존 클래스에 메소드를 추가할 수 있습니다.
이를 확장 함수라고 합니다.
확장 함수는 static 메소드 입니다.
자바의 String 클래스에 lastChar() 라는 메소드를 추가해서 "zerog".lastChar() 처럼 기존 클래스의 메소드인것처럼 사용할 수 있습니다.

fun String.lastChar():Char = this.get(this.length - 1)

//this 생략 가능
fun String.lastChar():Char = get(length - 1)

String 자바 클래스에는 lastChar() 라는 메소드가 없지만, 확장 함수로 생성해서 String 클래스의 메소드인것처럼 사용합니다.
  • String.lastChar() 중 String 수신 객체 타입(receiver type) 이라고 합니다.
  • lastChar():Char 중 lastChar() 확장 함수의 이름이고 :Char반환 타입 입니다.
  • this.get(this.length - 1) 중 this 수신 객체(receiver object) 라고 합니다.
  • 확장 함수의 블럭 안에서는 this.get(this.length - 1) 처럼 원래 갖고 있던 메소드를 호출할 수도 있으며 this 생략이 가능합니다.
  • 단, 클래스의 메소드 중 private, protected 접근자인 경우 호출할 수 없습니다.
  • 확장 함수 이름은 짧게 하는 것이 좋습니다.

제네릭을 이용하면 좀 더 유연한 확장 함수가 됩니다.

fun <T> Collection<T>.joinToString(separator: String = ",",
                                   prefix: String = "(",
                                   postfix: String = ")"
): String {

//사용
val list = listOf("1","2","3")
list.joinToString()

제네릭 중 특정 타입만 사용하도록 할 수 있습니다.

//String 제네릭
fun Collection<String>.join(separator: String = ",",
                                   prefix: String = "(",
                                   postfix: String = ")"
): String {
  ....
}

//사용
val intlist = listOf(1,2,3,4)
intlist.join() //에러!!! Int 타입은 불가능하다

확장 함수 오버라이드

확장 함수는 static 메소드이기 때문에 오버라이드를 할 수 없습니다.
그리고 확장 함수는 클래스 밖에 선언되기 때문에 오버라이드를 할 수 없습니다.

예를 들어 View 를 상속한 Button 이 있고 View.showOff() 확장 함수를 추가하고 Button.showOff() 에도 확장 함수를 추가한 경우에 오버라이드 되지 않습니다.

//View.showOff() 확장 함수 존재
//Button.showOff() 확장 함수 존재

val view : View = Button(this) //View 를 상속받은 Button 클래스
view.showOff() //오버라이드된것 같지만 View.showOff() 가 호출 됨

val button:Button = Button(this)
button.showOff() //Button.showOff() 가 호출 됨

중복된 확장 함수

확장 함수 이름이 같은 경우 import 할 때 이름을 바꿔야합니다.

StringJoin.kt //StringJoin.kt 파일에 정의된 확장함수

package com.zerogdev.mykotlin.function.other
fun String.lastChar():Char = this.get(this.length - 1)


ExtendFunction.kt //ExtendFunction.kt 파일에 정의된 확장함수

package com.zerogdev.mykotlin.function.extend
fun String.lastChar():Char = this.get(this.length - 1)

위 처럼 StringJoin.kt 와 ExtendFunction.kt 에 같은 확장 함수 이름으로 lastChar() 를 추가한경우 lastChar() 를 사용할때 "... cannot be invoked as a function." 이라는 에러가 발생합니다.

그런경우 import 할 때 as 를 사용해서 [import com.zerog.extend as 변경할 이름] 처럼 메소드 이름을 변경해서 사용하면 에러를 해결할 수 있습니다.

import com.zerogdev.mykotlin.function.other.lastChar
import com.zerogdev.mykotlin.function.extend.lastChar as last //last 로 이름을 변경

//확장 함수 사용
"zerog".lastChar() //other 패키지에 있는 lastChar() 사용
"zerog".last()       //extend 패키지에 있는 lastChar() 사용

확장 함수 이름과 클래스의 멤버 함수 이름이 같은 경우

확장 함수와 클래스 멤버 함수의 이름이 같은경우 멤버 함수의 우선순위가 더 높기때문에 클래스 내의 멤버 함수가 호출됩니다.

그렇기때문에 클래스의 메소드를 추가하거나 이름을 수정할 경우 주의가 필요합니다.
왜냐하면 사용하고 있던 확장 함수 이름과 같은 클래스 메소드가 추가될경우 잘 사용하고 있던 확장 함수가 호출되지 않고 클래스의 메소드가 호출될 수 있기때문입니다.

//클래스 메소드
fun hello() {
    log("hello-class")
}

//확장 함수지만 클래스 메소드에도 같은 hello 이름이 있기때문에
//클래스의 멤버 함수가 호출 된다.
//이 확장 함수는 절대로 호출되지 않는다.
fun FunctionUitl.hello() {
    log("hello-extend")
}

자바에서 확장 함수 호출

확장 함수는 수신 객체를 인자로 받는 static 메소드입니다.
자바에서는 코틀린 파일에 Kt가 붙기때문에 StringJoinKt 의 static 메소드를 호출합니다.
수신 객체 타입은 String 이며 인자에는 수신 객체인 문자열을 전달합니다.

StringJoinKt.lastChar("zerog") //자바에서는 static 메소드인 lastChar() 에 수신 객체                                               "zerog" 를 인자로 전달

확장 프로퍼티

프로퍼티도 확장할 수 있습니다.
다만, 확장 프로퍼티는 상태를 저장할 수 없기때문에 초기화 할 수 없고 get() 을 구현 해야됩니다.

그리고 List 처럼 상태를 저장할 수 있는 클래스인경우 get()과 set()을 추가할 수 있습니다.

//get() 구현
val String.lastChar: Char
get() = get(length - 1)

//리스트인경우 get(), set() 구현
var List.lastChar: String
get() { return last()}
set(value) {
    var lastChar: String = last()
    lastChar = value
}


//코틀린에서 사용
"zerog".lastChar        //확장 프로퍼티 호출

val list = listOf("a", "b", "c")
list.lastChar              //get()
list.lastChar = "d"      //set()

//자바에서 사용
StringUtilKt.getLastChar("zerog");


댓글

이 블로그의 인기 게시물

코틀린 (Kotlin) filter, map, all, any, count, find, groupBy, flatMap 함수 정리

코틀린 (Kotlin) 인터페이스 정리

코틀린 (Kotlin) object 키워드와 동반 객체 정리