Kotlin learning: Kotlin extension

Kotlin extension

Kotlin can extend the properties and methods of a class without inheriting or using the Decorator pattern.

Expansion is a static behavior and will not cause any impact on the extended class code itself.

Extension function

The extension function can add new methods to the existing class without modifying the original class. The extension function definition form

fun receiverType.functionName(params){
    body
}
  • receiverType: indicates the receiver of the function, that is, the object of the function extension
  • functionName: the name of the extension function
  • params: the parameters of the extension function, which 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("Runoob")
    user.Print()
}

Instance execution output result

用户名 Runoob

The following code adds a swap function to MutableList

// 扩展函数 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 output of the instance execution is

[3, 2, 1]

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

Extension functions are statically resolved

The extension function is statically resolved, not a virtual member of the receiver type. When the extension function is called, the specific function that is called is determined by the object expression of the calling function, rather than the dynamic type. of

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 output of the instance execution is

c

If the extension function and the member function are the same, the member function will be used first when the function is used

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

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

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

The output of the instance execution is

成员函数

Extend an empty object

In the extension function, you can use this to determine whether the receiver is NULL, so that even if the receiver is NULL, you can call the extension function. E.g

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 output of the instance execution is

null

Extended attributes

In addition to functions, Kotlin also supports attributes to extend attributes

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

Extended attributes are allowed to be defined in classes or kotlin files, but not in functions. Initialization properties are not allowed to be initialized because they have no backing field, and can only be defined by explicitly provided getters/setters

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

Extended attributes can only be declared as val

Expansion of companion objects

If a class definition has a companion object, you can also define extension functions and attributes for the companion object.

The companion object calls the companion object through the form of "class name.", and the extension function declared by the companion object is called by 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 output of the instance execution is

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

Extended scope

Usually extension functions or attributes are defined under the top-level package

package foo.bar

fun Baz.goo() { …… } 

To use an extension outside the defined package, import the function name of the extension through import for use

package com.example.usage

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

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

Extended declaration as a member

Within a class you can declare extensions for another class.

In this extension, there are multiple implicit recipients. The instance of the class where the extension method is defined is called the distribution recipient, and the instance of the target type of the extension method is called the extension recipient.

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 output of the instance execution is

D bar
C baz

Within the C class, an extension of the D class is created. At this point, C is the distribution recipient, and D is the extension recipient. From the above example, it can be clearly seen that in the extension function, the member function of the dispatch receiver can be called.

If you are calling a function, and the function exists in both the distribution receiver and the extended receiver, the extended receiver takes precedence. To refer to the members of the distribution receiver, you can use the limited this syntax

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

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

    fun D.foo() {
        bar()         // 调用 D.bar(),扩展接收者优先
        [email protected]()  // 调用 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 output of the instance execution is

D bar
C bar

The extension function defined in the form of a member can be declared as open and can be overridden in subclasses. That is to say, in the dispatch process of this type of extension function, it is virtual for the recipient of the distribution, but for the extension The receiver is still 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()   // 调用扩展函数
    }
}

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 output of the instance execution is

D.foo in C
D.foo in C1
D.foo in C

The members in the companion object are equivalent to the static members in Java. Its life cycle is always accompanied by the class. Variables and functions can be defined in the companion object. These variables and functions can be directly referenced by the class name.

For the companion object extension function, there are two forms, one is to expand within the class, and the other is to expand outside of the class. The expanded functions of these two forms do not affect each other (even the name can be the same), even if the name is the same, They are also completely two different functions, and have the following characteristics:

  •  (1) The companion object function of the expansion within the class and the companion object of the expansion outside the class can have the same name. They are two independent functions and do not affect each other;
  •  (2) When the companion object function expanded in the class and the companion object expanded outside the class have the same name, other functions in the class preferentially refer to the companion object function expanded in the class, that is, for other member functions in the class, the expansion shield in the class Out-of-class extension
  •  (3) Companion object functions extended within a class can only be referenced by functions within the class, and cannot be referenced by functions outside the class and functions in the companion object;
  •  (4) The companion object function extended outside the class can be referenced by the function in the companion object
class MyClass {
    companion object {
        val myClassField1: Int = 1
        var myClassField2 = "this is myClassField2"
        fun companionFun1() {
            println("this is 1st companion function.")
            foo()
        }
        fun companionFun2() {
            println("this is 2st companion function.")
            companionFun1()
        }
    }
    fun MyClass.Companion.foo() {
        println("伴随对象的扩展函数(内部)")
    }
    fun test2() {
        MyClass.foo()
    }
    init {
        test2()
    }
}
val MyClass.Companion.no: Int
    get() = 10
fun MyClass.Companion.foo() {
    println("foo 伴随对象外部扩展函数")
}
fun main(args: Array<String>) {
    println("no:${MyClass.no}")
    println("field1:${MyClass.myClassField1}")
    println("field2:${MyClass.myClassField2}")
    MyClass.foo()
    MyClass.companionFun2()
}

operation result

no:10
field1:1
field2:this is myClassField2
foo 伴随对象外部扩展函数
this is 2st companion function.
this is 1st companion function.
foo 伴随对象外部扩展函数

 

Guess you like

Origin blog.csdn.net/PrisonJoker/article/details/113764282