Kotlin Extensions

Kotlin can extend the properties and methods of a class without the need for inheritance or the Decorator pattern.

Extension is a static behavior and does not affect the code of the extended class itself.


Extension Functions

Extension functions can add new methods to existing classes without modifying the original class. The definition form of an extension function is:

fun receiverType.functionName(params){
    body
}
  • receiverType: indicates the receiver of the function, that is, the object being extended by the function
  • functionName: the name of the extension function
  • params: parameters of the extension function, can be NULL

The following example extends the User class:

class User(var name:String)

/**扩展函数**/
fun User.Print(){
    print("用户名 $name")
}

fun main(arg:Array<String>){
    var user = User("Example")
    user.Print()
}

The execution output of the example is:

用户名 Example

The following code is for MutableListto add a swap function:

// 扩展函数 swap,调换不同位置的值
fun MutableList<Int>.swap(index1: Int, index2: Int) {
    val tmp = this[index1]     //  this 对应该列表
    this[index1] = this[index2]
    this[index2] = tmp
}

fun main(args: Array<String>) {

    val l = mutableListOf(1, 2, 3)
    // 位置 0 和 2 的值做了互换
    l.swap(0, 2) // 'swap()' 函数内的 'this' 将指向 'l' 的值

    println(l.toString())
}

The execution output of the example is:

[3, 2, 1]

The this keyword refers to the receiver object (that is, the object instance specified before the dot when calling an extension function).


Extension Functions Are Resolved Statically

Extension functions are resolved statically; they are not virtual members of the receiver type. When calling an extension function, which function is specifically called is determined by the object expression calling the function, not by dynamic type:

open class C

class D: C()

fun C.foo() = "c"   // 扩展函数 foo

fun D.foo() = "d"   // 扩展函数 foo

fun printFoo(c: C) {
    println(c.foo())  // 类型是 C 类
}

fun main(arg:Array<String>){
    printFoo(D())
}

The execution output of the example is:

c

If an extension function and a member function are identical, the member function is used in preference when using that function.

class C {
    fun foo() { println("成员函数") }
}

fun C.foo() { println("扩展函数") }

fun main(arg:Array<String>){
    var c = C()
    c.foo()
}

The execution output of the example is:

Member function

Extending a Null Object

Inside an extension function, you can use this to determine whether the receiver is NULL. In this way, even if the receiver is NULL, the extension function can still be called. For example:

fun Any?.toString(): String {
    if (this == null) return "null"
    // 空检测之后,“this”会自动转换为非空类型,所以下面的 toString()
    // 解析为 Any 类的成员函数
    return toString()
}
fun main(arg:Array<String>){
    var t = null
    println(t.toString())
}

The execution output of the example is:

null

Extension Properties

Besides functions, Kotlin also supports extending properties with properties:

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

Extension properties can be defined in classes or Kotlin files, but cannot be defined in functions. Because extension properties have no backing field, they cannot be initialized; they can only be defined by explicitly provided getters/setters.

val Foo.bar = 1 // 错误:扩展属性不能有初始化器

Extension properties can only be declared as val.


Extensions for Companion Objects

If a class defines a companion object, you can also define extension functions and properties for the companion object.

A companion object is called in the form of "Class.". Extension functions declared for a companion object are called using the class name qualifier:

class MyClass {
    companion object { }  // 将被称为 "Companion"
}

fun MyClass.Companion.foo() {
    println("伴随对象的扩展函数")
}

val MyClass.Companion.no: Int
    get() = 10

fun main(args: Array<String>) {
    println("no:${MyClass.no}")
    MyClass.foo()
}

The execution output of the example is:

no:10
伴随对象的扩展函数

Scope of Extensions

Usually extension functions or properties are defined in a top-level package:

package foo.bar

fun Baz.goo() { …… } 

To use an extension from outside the package in which it is defined, import the extension function name via import:

package com.example.usage

import foo.bar.goo // 导入所有名为 goo 的扩展
                   // 或者
import foo.bar.*   // 从 foo.bar 导入一切

fun usage(baz: Baz) {
    baz.goo()
}

Extensions Declared as Members

Inside a class, you can declare extensions for another class.

In such an extension, there are multiple implicit receivers. The instance of the class where the extension method is defined is called the dispatch receiver, and the instance of the target type of the extension method is called the extension receiver.

class D {
    fun bar() { println("D bar") }
}

class C {
    fun baz() { println("C baz") }

    fun D.foo() {
        bar()   // 调用 D.bar
        baz()   // 调用 C.baz
    }

    fun caller(d: D) {
        d.foo()   // 调用扩展函数
    }
}

fun main(args: Array<String>) {
    val c: C = C()
    val d: D = D()
    c.caller(d)

}

The execution output of the example is:

D bar
C baz

Inside class C, an extension for class D is created. At this point, C is called the dispatch receiver, and D is the extension receiver. From the above example, it can be clearly seen that in an extension function, you can call member functions of the dispatch receiver.

If a function is called and it exists in both the dispatch receiver and the extension receiver, the extension receiver takes precedence. To reference members of the dispatch receiver, you can use the qualified this syntax.

class D {
    fun bar() { println("D bar") }
}

class C {
    fun bar() { println("C bar") }  // 与 D 类 的 bar 同名

    fun D.foo() {
        bar()         // 调用 D.bar(),扩展接收者优先
        this@C.bar()  // 调用 C.bar()
    }

    fun caller(d: D) {
        d.foo()   // 调用扩展函数
    }
}

fun main(args: Array<String>) {
    val c: C = C()
    val d: D = D()
    c.caller(d)

}

The execution output of the example is:

D bar
C bar

Extension functions defined as members can be declared as open and can be overridden in subclasses. That is, in the dispatch process of such extension functions, they are virtual for the dispatch receiver, but are still static for the extension receiver.

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()   // 调用扩展函数
    }
}

class C1 : C() {
    override fun D.foo() {
        println("D.foo in C1")
    }

    override fun D1.foo() {
        println("D1.foo in C1")
    }
}


fun main(args: Array<String>) {
    C().caller(D())   // 输出 "D.foo in C"
    C1().caller(D())  // 输出 "D.foo in C1" —— 分发接收者虚拟解析
    C().caller(D1())  // 输出 "D.foo in C" —— 扩展接收者静态解析

}

The execution output of the example is:

D.foo in C
D.foo in C1
D.foo in C
Other Extensions