공감 및 댓글은 포스팅 하는데 아주아주 큰 힘이 됩니다!! 포스팅 내용이 찾아주신 분들께 도움이 되길 바라며 더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^
|
이번 포스팅에서는 Extension 에 대해서 공부합니다.
코틀린은 클래스의 상속이나, Decorator 같은 디자인 패턴을 사용하지 않고,
클래스에 새로운 메서드를 확장할 수 있도록 제공한다는 점에서
C# 이나 Gosu 언어와 유사합니다.
Extention functions
클래스 또는 인터페이스에 메서드를 확장하는 방법은 간단합니다.
fun 클래스이름.메소드이름(매개변수...) {
// 코드....
}
MutableList<T> 에 swap 이라는 메서드를 확장하는 예제입니다.
fun main(args: Array<String>) {
val l = mutableListOf(1, 2, 3)
l.swap(1,2)
}
fun MutableList<Int>.swap(index1 : Int, index2 : Int) {
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
원래는 swap 이라는 메서드가 없지만, Extension Functions 로 인해 swap 함수를 사용할 수
있게 되었습니다.
위처럼 꼭 타입을 지정하지 않아도 됩니다.
fun main(args: Array<String>) {
val s = mutableListOf("a", "b", "c")
s.swap2(0,1)
println(s)
}
fun <T> MutableList<T>.swap2(index1: Int, index2: Int) {
val tmp = this[index1] // 'this' corresponds to the list
this[index1] = this[index2]
this[index2] = tmp
}
타입을 제네릭으로 선언하고, 실제 사용할 때에 타입이 정해져도 됩니다.
---------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------
Extensions 는 정적으로 해결됩니다?
Extensions을 사용한다고해서 실제로 그 클래스나 인터페이스가 수정되는 것은 아닙니다.
Extension을 정의하면, 클래스에 새 멤버로 추가되는 것이 아니라,
.(콤마) 로 호출할 수 있도록 새 함수를 만들기만 하는 것입니다.
Extension 런타임 시 해당 메서드에 입력된 타입을 확인하는 것이 아니라,
Extension 선언 시 입력된 타입으로 결과가 나옵니다.
이게 무슨 말이냐 하면 아래 예제를 보겠습니다.
fun main(args: Array<String>) {
printFoo(Child())
}
open class Parent
class Child: Parent()
fun Parent.foo() = "parent"
fun Child.foo() = "child"
fun printFoo(parent: Parent) {
println(parent.foo())
}
위와 같은 예제가 있다고 할 때, 출력결과가 child 가 나올 것 같지만,
실제로는 parent로 나옵니다.
만약 아래와 같은 클래스처럼 멤버의 이름과 Extensions의 이름이 같다면
항상 멤버가 우선순위를 가집니다.
fun main(args: Array<String>) {
val parent = Parent()
parent.PPP()
}
open class Parent {
fun PPP() {
print("PPAP")
}
}
fun Parent.PPP() {
print("PPPA")
}
하지만, 멤버와 Extension의 이름은 같으나, 파라미터가 다르다면, 다른 것으로
간주됩니다. ( 뭐 당연한 얘기겠죠?)
Nullable Receiver
Extensions을 정의할 때 Nullable Receiver으로도 정의될 수 있습니다.
fun main(args: Array<String>) {
val a :String? = "aaa"
a.toString()
}
fun Any?.toString(): String {
if (this == null) return "null"
return toString()
}
위처럼 Extension을 정의한다면 null에서 자유롭게 toString() 함수를 사용할 수 있습니다.
Extension Properties
메서드와 유사하게, 프로퍼티도 확장이 가능합니다.
fun main(args: Array<String>) {
val list = listOf("a", "b", "c", "d")
print(list.lastIndex)
}
val <T> List<T>.lastIndex: Int
get() { return size - 1 }
프로퍼티에 Extension을 선언하려면, 초기화 값이 있으면 안됩니다.
Extensions를 멤버로 선언
클래스 내부에 다른 클래스의 Extension을 선언할 수 있습니다.
그리고 Extension 안에선 다른 클래스의 멤버를 별다른 선언 없이,
멤버의 이름으로 바로 호출할 수 있습니다.
fun main(args: Array<String>) {
print(C().caller(D()))
}
class D {
fun ddd() {
println("ddd")
}
}
class C {
fun ccc() {
println("ccc")
}
fun D.aaa() {
ddd() // calls D.bar
ccc() // calls C.baz
}
fun caller(d: D) {
d.aaa() // call the extension function
}
}
만약 클래스에 open 키워드를 사용했고, 클래스에 Extensions 을 했다면,
이 클래스를 상속받은 자식 클래스는 Extensions를 override 할 수 있습니다.
open class D {
}
class D1 : D() {
}
open class C {
open fun D.foo() {
println("D.foo in C")
}
open fun D1.foo() {
println("D1.foo in C")
}
fun caller(d: D) {
d.foo() // call the extension function
}
}
class C1 : C() {
override fun D.foo() {
println("D.foo in C1")
}
override fun D1.foo() {
println("D1.foo in C1")
}
}
이상 Extension에 대해 공부했습니다.
감사합니다.
다음 포스팅에서는 Data Class 와 Sealed Class 에 대해 공부합니다.
'코틀린(Kotlin)' 카테고리의 다른 글
코틀린(kotlin) : Nested Class, inner class, Enum Class 중첩, 내부, Enum 클래스 (0) | 2018.02.05 |
---|---|
코틀린(kotlin) : Data Class 와 Sealed Class (0) | 2018.01.31 |
코틀린(kotlin) : 접근 제한자 - private, protected, internal, public (0) | 2018.01.31 |
코틀린(kotlin) : 인터페이스 Interface (0) | 2018.01.30 |
코틀린(kotlin) : 프로퍼티와 필드 Properties and Fields (0) | 2018.01.30 |