Hanbit the Developer

Kotlin Documentation | Extensions 본문

Kotlin

Kotlin Documentation | Extensions

hanbikan 2023. 5. 22. 18:16

Kotlin Documentation 시리즈에 대해

Category: Concepts - Classes and objects

문서 링크: https://kotlinlang.org/docs/extensions.html


데코레이터 패턴, 상속 없이 클래스, 인터페이스를 확장할 수 있다:

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
}

Extensions are resolved statically

멤버를 추가하는 것이 아니라, statically하게 작동한다.(런타임 때의 타입으로 작동하는 게 아니다.):

open class Shape
class Rectangle: Shape()

fun Shape.getName() = "Shape"
fun Rectangle.getName() = "Rectangle"

fun printClassName(s: Shape) {
    println(s.getName())
}

printClassName(Rectangle())
Shape

런타임에서 Rectangle 인스턴스가 함수가 들어갔음에도 Shape.getName()을 호출한다.

같은 함수가 있을 때 멤버 함수의 우선순위가 더 높다:

class Example {
    fun printFunctionType() { println("Class method") }
}

fun Example.printFunctionType() { println("Extension function") }

Example().printFunctionType()
Class method

Nullable receiver

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

val <T> List<T>.lastIndex: Int
    get() = size - 1

val House.number = 1 // error: initializers are not allowed for extension properties

Companion object extensions

fun MyClass.Companion.printCompanion() { println("companion") }

Scope of extensions

대부분 패키지의 top-level에 확장 함수를 선언한다:

package org.example.declarations

fun List<String>.getLongestString() { /*...*/}

이를 사용하기 위해 import가 필요하다:

package org.example.usage

import org.example.declarations.getLongestString

fun main() {
    val list = listOf("red", "green", "blue")
    list.getLongestString()
}

Declaring extensions as members

class Host(val hostname: String) {
    fun printHostname() { print(hostname) }
}

class Connection(val host: Host, val port: Int) {
    fun printPort() { print(port) }

    **fun Host.printConnectionString() {
        printHostname()   // calls Host.printHostname()
        print(":")
        printPort()   // calls Connection.printPort()
    }**

    fun connect() {
        /*...*/
        **host.printConnectionString()   // calls the extension function**
    }
}

fun main() {
    **Connection(Host("kotl.in"), 443).connect()
    // Host("kotl.in").printConnectionString()  // error, the extension function is unavailable outside Connection**
}
class Connection {
    fun Host.getConnectionString() {
        toString()         // calls Host.toString()
        this@Connection.toString()  // calls Connection.toString()
    }
}
open class Base { }

class Derived : Base() { }

open class BaseCaller {
    open fun Base.printFunctionInfo() {
        println("Base extension function in BaseCaller")
    }

    open fun Derived.printFunctionInfo() {
        println("Derived extension function in BaseCaller")
    }

    fun call(b: Base) {
        b.printFunctionInfo()   // call the extension function
    }
}

class DerivedCaller: BaseCaller() {
    override fun Base.printFunctionInfo() {
        println("Base extension function in DerivedCaller")
    }

    override fun Derived.printFunctionInfo() {
        println("Derived extension function in DerivedCaller")
    }
}

fun main() {
    BaseCaller().call(Base())   // "Base extension function in BaseCaller"
    BaseCaller().call(Derived()) // "Base extension function in BaseCaller"
    DerivedCaller().call(Base())  // "Base extension function in DerivedCaller" - dispatch receiver is resolved virtually
    DerivedCaller().call(Derived())  // "Base extension function in DerivedCaller" - extension receiver is resolved statically
}