TIL

Extensions

Extension functions

// MutableList<Int>가 수신 객체 타입이 된다.

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

open class Shape
class Rectangle : Shape()

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

fun main() {
	val shape: Shape = Rectangle()
	println(shape.getName()) // 결과는 "Shape"
}
class Shape {
    fun getName() = "멤버 함수 Shape"
}

fun Shape.getName() = "확장 함수 Shape"

val s = Shape()
println(s.getName()) // 결과는 "멤버 함수 Shape"
class Example {
    fun printFunctionType() { println("Class method") }
}

fun Example.printFunctionType(i: Int) { println("Extension function #$i") }

Example().printFunctionType(1) // "Extension function 1"

Nullable receiver

fun Any?.toString(): String {
    if (this == null) return "null"
    return toString()
}

Extension properties

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

Companion object extensions

class MyClass {
    companion object { }
}

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

fun main() {
    MyClass.printCompanion()
}

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() // Host의 멤버 호출
        print(":")
        printPort()     // Connection의 멤버 호출
    }

    fun connect() {
        host.printConnectionString()
    }
}

fun main() {
    Connection(Host("kotl.in"), 443).connect()
    // 결과: kotl.in:443
    // Host("kotl.in").printConnectionString(443) // 오류: 확장 함수는 Connection 안에서만 호출 가능
}

class Connection {
    fun Host.getConnectionString() {
        toString()         // Host.toString() 호출
        this@Connection.toString()  // 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()
    }
}

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"
    DerivedCaller().call(Base()) // "Base extension function in DerivedCaller"
    DerivedCaller().call(Derived()) // "Base extension function in DerivedCaller" (여기가 포인트)
}

Note on visibility