코틀린 (Kotlin) 확장 함수와 확장 프로퍼티
코틀린 확장 함수
코틀린는 기존 클래스에 메소드를 추가할 수 있습니다.이를 확장 함수라고 합니다.
확장 함수는 static 메소드 입니다.
자바의 String 클래스에 lastChar() 라는 메소드를 추가해서 "zerog".lastChar() 처럼 기존 클래스의 메소드인것처럼 사용할 수 있습니다.
fun String.lastChar():Char = this.get(this.length - 1)
//this 생략 가능
fun String.lastChar():Char = get(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()
prefix: String = "(",
postfix: String = ")"
): String {
list.joinToString()
제네릭 중 특정 타입만 사용하도록 할 수 있습니다.
//String 제네릭
fun Collection<String>.join(separator: String = ",",
prefix: String = "(",
postfix: String = ")"
): String {
....
}
//사용
val intlist = listOf(1,2,3,4)
intlist.join() //에러!!! Int 타입은 불가능하다
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() 가 호출 됨
//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)
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() 사용
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")
}
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");
val String.lastChar: Char
get() = get(length - 1)
//리스트인경우 get(), set() 구현
var List
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");
댓글
댓글 쓰기