Extensions

    To declare an extension function, we need to prefix its name with a receiver type, i.e. the type being extended. The following adds a function to MutableList<Int>:

    The this keyword inside an extension function corresponds to the receiver object (the one that is passed before the dot). Now, we can call such a function on any MutableList<Int>:

    1. val list = mutableListOf(1, 2, 3)
    2. list.swap(0, 2) // 'this' inside 'swap()' will hold the value of 'list'

    Of course, this function makes sense for any MutableList<T>, and we can make it generic:

    1. fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
    2. val tmp = this[index1] // 'this' corresponds to the list
    3. this[index1] = this[index2]
    4. this[index2] = tmp
    5. }

    We declare the generic type parameter before the function name for it to be available in the receiver type expression. See Generic functions.

    Extensions are resolved statically

    Extensions do not actually modify classes they extend. By defining an extension, you do not insert new members into a class, but merely make new functions callable with the dot-notation on variables of this type.

    We would like to emphasize that extension functions are dispatched statically, i.e. they are not virtual by receiver type. This means that the extension function being called is determined by the type of the expression on which the function is invoked, not by the type of the result of evaluating that expression at runtime. For example:

    1. fun main() {
    2. //sampleStart
    3. open class Shape
    4. class Rectangle: Shape()
    5. fun Shape.getName() = "Shape"
    6. fun Rectangle.getName() = "Rectangle"
    7. fun printClassName(s: Shape) {
    8. println(s.getName())
    9. }
    10. printClassName(Rectangle())
    11. //sampleEnd
    12. }

    If a class has a member function, and an extension function is defined which has the same receiver type, the same name, and is applicable to given arguments, the member always wins. For example:

    1. fun main() {
    2. //sampleStart
    3. class Example {
    4. fun printFunctionType() { println("Class method") }
    5. }
    6. fun Example.printFunctionType() { println("Extension function") }
    7. Example().printFunctionType()
    8. //sampleEnd
    9. }

    This code prints “Class method“.

    However, it’s perfectly OK for extension functions to overload member functions which have the same name but a different signature:

    Note that extensions can be defined with a nullable receiver type. Such extensions can be called on an object variable even if its value is null, and can check for this == null inside the body. This is what allows you to call toString() in Kotlin without checking for null: the check happens inside the extension function.

    1. if (this == null) return "null"
    2. // resolves to the member function of the Any class
    3. return toString()
    4. }

    Extension properties

    Similarly to functions, Kotlin supports extension properties:

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

    Note that, since extensions do not actually insert members into classes, there’s no efficient way for an extension property to have a . This is why initializers are not allowed for extension properties. Their behavior can only be defined by explicitly providing getters/setters.

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

    If a class has a companion object defined, you can also define extension functions and properties for the companion object. Just like regular members of the companion object, they can be called using only the class name as the qualifier:

    1. class MyClass {
    2. companion object { } // will be called "Companion"
    3. }
    4. fun MyClass.Companion.printCompanion() { println("companion") }
    5. fun main() {
    6. MyClass.printCompanion()
    7. }

    Scope of extensions

    Most of the time we define extensions on the top level - directly under packages:

    To use such an extension outside its declaring package, we need to import it at the call site:

    1. package org.example.usage
    2. import org.example.declarations.getLongestString
    3. fun main() {
    4. val list = listOf("red", "green", "blue")
    5. list.getLongestString()
    6. }

    See Imports for more information.

    Inside a class, you can declare extensions for another class. Inside such an extension, there are multiple implicit receivers - objects members of which can be accessed without a qualifier. The instance of the class in which the extension is declared is called dispatch receiver, and the instance of the receiver type of the extension method is called extension receiver.

    1. class Host(val hostname: String) {
    2. fun printHostname() { print(hostname) }
    3. }
    4. class Connection(val host: Host, val port: Int) {
    5. fun printPort() { print(port) }
    6. fun Host.printConnectionString() {
    7. printHostname() // calls Host.printHostname()
    8. print(":")
    9. printPort() // calls Connection.printPort()
    10. }
    11. fun connect() {
    12. /*...*/
    13. host.printConnectionString() // calls the extension function
    14. }
    15. }
    16. fun main() {
    17. //Host("kotl.in").printConnectionString(443) // error, the extension function is unavailable outside Connection
    18. }

    In case of a name conflict between the members of the dispatch receiver and the extension receiver, the extension receiver takes precedence. To refer to the member of the dispatch receiver you can use the .

    1. class Connection {
    2. fun Host.getConnectionString() {
    3. toString() // calls Host.toString()
    4. this@Connection.toString() // calls Connection.toString()
    5. }
    6. }
    1. open class Base { }
    2. class Derived : Base() { }
    3. open class BaseCaller {
    4. open fun Base.printFunctionInfo() {
    5. println("Base extension function in BaseCaller")
    6. }
    7. open fun Derived.printFunctionInfo() {
    8. println("Derived extension function in BaseCaller")
    9. }
    10. fun call(b: Base) {
    11. b.printFunctionInfo() // call the extension function
    12. }
    13. }
    14. class DerivedCaller: BaseCaller() {
    15. override fun Base.printFunctionInfo() {
    16. println("Base extension function in DerivedCaller")
    17. }
    18. override fun Derived.printFunctionInfo() {
    19. println("Derived extension function in DerivedCaller")
    20. }
    21. }
    22. fun main() {
    23. BaseCaller().call(Base()) // "Base extension function in BaseCaller"
    24. DerivedCaller().call(Base()) // "Base extension function in DerivedCaller" - dispatch receiver is resolved virtually
    25. DerivedCaller().call(Derived()) // "Base extension function in DerivedCaller" - extension receiver is resolved statically
    26. }

    Note on visibility

    Extensions utilize the same as regular functions declared in the same scope would. For example:

    • If an extension is declared outside its receiver type, such an extension cannot access the receiver’s private members.