본문
170828(월) - Kotlin docs (Extensions)
Kotlin docs
Extensions
- Kotlin supports extension functions & extension properties
Extension Functions
- receiver type을 선언해야한다.
- this 키워드는 receiver object
fun MutableList<Int>.swap(index1: Int, index2: Int) { val tmp = this[index1] // 'this' corresponds to the list this[index1] = this[index2] this[index2] = tmp }
val l = mutableListOf(1, 2, 3) l.swap(0, 2) // 'this' inside 'swap()' will hold the value of 'l'
- Generic도 사용 가능하다.
fun <T> MutableList<T>.swap(index1: Int, index2: Int) { val tmp = this[index1] // 'this' corresponds to the list this[index1] = this[index2] this[index2] = tmp }
Extension are resolved statically
- class에 새 member를 삽입하지 않고 dot-notation으로 call 가능한 새 function을 만든다.
- extension functions는 statically하게 전달된다
→ receiver type에 의해서 virtual하지 않다.
- extension functions는 function이 호출되는 expression type에 의해서 결정
→ runtime에 expression을 평가한 결과의 type에 의해서가 아니다.
open class C class D: C() fun C.foo() = "c" fun D.foo() = "d" fun printFoo(c: C) { println(c.foo()) } printFoo(D())
- "c"가 프린트 된다.
→ 호출되는 extension function은 C class의 parameter c의 type에만 의존
- class에 member function을 가지고 있고 동일한 receiver type을 가진 extension function가 정의되어 있다면, same name을 사용하고 주어진 parameter에 적용할 수 있는 경우 memeber가 항상 win이다.
class C { fun foo() { println("member") } } fun C.foo() { println("extension") }
ㆍ"member"가 프린트 된다.
- extension function이 이름은 같지만 signature가 다른 member function을 overload하는것은 괜찮다.
class C { fun foo() { println("member") } } fun C.foo(i: Int) { println("extension") }
Nullable Recevier
- extensions는 nullable receiver type으로 정의될 수 있다.
- 이러한 extension은 value가 null인 경우에도 object variable을 call 가능하며 body에서 this == null을 확인 할 수 있다.
fun Any?.toString(): String { if (this == null) return "null" // after the null check, 'this' is autocast to a non-null type, so the toString() below // resolves to the member function of the Any class return toString() }
Extension Properties
- 실제로 member들을 class에 insert하지 않기 때문에 backing field를 가지는 효율적인 방법이 없다.
→ initalizer가 extension properties를 허용하지 않는 이유
→ getter / setter 사용
val <T> List<T>.lastIndex: Int get() = size - 1
val Foo.bar = 1 // error: initializers are not allowed for extension properties
Companion Object Extensions
- 만약 class가 companion object를 가지고 있다면, companion object의 extension function과 properties를 정의 가능하다.
class MyClass { companion object { } // will be called "Companion" } fun MyClass.Companion.foo() { // ... }
- companion object의 regular member와 마찬가지로 class name 만 qualifier로 호출 가능하다.
MyClass.foo()
Scope of Extensions
- 대부분의 경우 extension을 top level (ex: directly under package)
package foo.bar fun Baz.goo() { ... }
- declaring package 외부에서 사용하려면 call site에서 import가 필요하다.
package com.example.usage import foo.bar.goo // importing all extensions by name "goo" // or import foo.bar.* // importing everything from "foo.bar" fun usage(baz: Baz) { baz.goo() }
Declaring Extensions as Members
- class내에서 다른 class의 extension을 선언 가능
- multiple implicit receivers가 있다.
- 이 receiver는 qualifier 없이 access가능
- extension이 선언된 class의 instance를 dispatch receiver 라고 한다.
- extension method의 receiver type의 instance를 extension receiver 라고 한다.
class D { fun bar() { ... } } class C { fun baz() { ... } fun D.foo() { bar() // calls D.bar baz() // calls C.baz } fun caller(d: D) { d.foo() // call the extension function } }
- dispatch receiver와 extension receiver간의 name conflict의 경우 extension receiver가 상위
- dispatch reciever를 참조하려면 정규화 구문을 사용
class C { fun D.foo() { toString() // calls D.toString() this@C.toString() // calls C.toString() }
- member로 선언된 extension은 open으로 선언 가능하고, sub class에서는 override 될 수 있다.
→ dispatch receiver는 virtual 이지만, extension receiver에 대해서는 static 이라는것을 의미
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") } } C().caller(D()) // prints "D.foo in C" C1().caller(D()) // prints "D.foo in C1" - dispatch receiver is resolved virtually C().caller(D1()) // prints "D.foo in C" - extension receiver is resolved statically
Motivation
- Java의 *Utils 나 java.util.Collections 등이 unpleasant 한 점은 다음과 같다.
// Java Collections.swap(list, Collections.binarySearch(list, Collections.max(otherList)), Collections.max(list))
- class name은 항상 이런식으로 가져온다. 이를 static import 시키면
// Java swap(list, binarySearch(list, max(otherList)), max(list))
- 조금 더 나아졌지만 IDE의 powerful code completion은 없다.
// Java list.swap(list.binarySearch(otherList.max()), list.max())
- 그러나 List class 안에 모든 메소드를 집어넣기를 원하는것은 아니다. 이럴때 extension이 도움이 될 것이다.
'Mobile > Kotlin' 카테고리의 다른 글
171211(월) - Using scope functions (0) | 2017.12.11 |
---|---|
171102(목) - Kotlin docs (Data Classes) (0) | 2017.11.03 |
170823(수) - Kotlin docs (Visibility Modifiers) (0) | 2017.08.23 |
170821(월) - Kotlin docs (Interfaces) (0) | 2017.08.21 |
170821(월) - Kotlin docs (Properties and Fields) (0) | 2017.08.21 |
댓글