Kotlin学习历程——扩展

Kotlin语言中文站

kotlin能够扩展一个类的新功能而无需继承该类,这种方式叫做扩展。例如:你可以为一个你不能修改的,来自第三方库中的类编写一个新的函数,这个新增的函数就像那个原始类本来就有的函数一样。


定义扩展函数

声明一个扩展函数,我们需要用一个接收者类型也就是被扩展的类型来作为他的前缀。

/** 在Extension.kt文件中定义扩展函数 */
fun <T> MutableList<T>.swap(index1: Int, index2: Int) {
    
    
    val temp = this[index1]
    this[index1] = this[index2]
    this[index2] = temp
}

//调用
import extension.mutablelist.swap

fun main(args: Array<String>) {
    
    
    val list = mutableListOf<Int>(1, 2, 3)
    list.swap(0,2)
    print(list)   //打印:3,2,1
}

看到list.swap(0,2)这行代码,我惊呆了…,我们将代码转成Java看看到底是个啥:

public final class ExtensionKt {
    
    
   public static final void swap(@NotNull List $this$swap, int index1, int index2) {
    
    
      Object temp = $this$swap.get(index1);
      $this$swap.set(index1, $this$swap.get(index2));
      $this$swap.set(index2, temp);
   }
}


public final class MainKt {
    
    
   public static final void main(@NotNull String[] args) {
    
    
      List list = CollectionsKt.mutableListOf(new Integer[]{
    
    1, 2, 3});
      ExtensionKt.swap(list, 0, 2);
      boolean var2 = false;
      System.out.print(list);
   }
}

这就明白了,简单的一个工厂模式,kotlin编译器最终会将list.swap(0,2)编译成ExtensionKt.swap(list, 0, 2)


扩展是静态解析的

扩展是静态解析的,扩展不能真正的修改它们所扩展的类,你定义了一个扩展,你并没有在一个类中插入新成员,仅仅是可以通过该类型的变量用点表达式去调用这个函数。

静态解析也就意味着调用扩展函数是由函数调用所在的表达式类型来决定的,而不是表达式运行时求值结果决定的。如下案例:

open class Shape

class Rectangle : Shape()

fun Shape.getName() = "Shape"

fun Rectangle.getName() = "Rectangle"

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

//调用
printClassName(Rectangle())  
//打印Shape。 因为调用扩展函数只取决于参数s的声明类型,该类型是Shape类。

还有种情况提一下,比如类中已经存在函数方法A(),你又给这个类声明了扩展函数A(),然后去调用这个函数,系统会调用哪个函数呢? 答案是类中的成员方法A(),成员方法优先级大于扩展函数。


属性的扩展

扩展属性跟扩展函数一样,是静态解析的,并没有实际将成员插入类中。 扩展属性是没有幕后字段的,也就不能有初始化器,它们的行为只能由显示提供的getters/setters定义。

var MutableList<String>.lastElement: String?
    get() {
    
    
        if(size > 0) {
    
    
            return get(size -1)
        }
        return null
    }
    set(value) {
    
    
        removeLast()
        add(value!!)
    }
    
    
/** 调用 */
import extension.mutablelist.lastElement

fun main(args: Array<String>) {
    
    
    val list = mutableListOf<String>("1", "2", "3")
    //取值
    println(list.lastElement)   //打印3

    //赋值
    list.lastElement = "4556"
    print(list.lastElement)  //打印4556
}

提供初始化器报错,如下:

我们也把kotlin代码转成java代码看看:

public final class ExtensionKt {
    
    
    @Nullable
   public static final String getLastElement(@NotNull List $this$lastElement) {
    
    
      return $this$lastElement.size() > 0 ? (String)$this$lastElement.get($this$lastElement.size() - 1) : null;
   }

   public static final void setLastElement(@NotNull List $this$lastElement, @Nullable String value) {
    
    
      CollectionsKt.removeLast($this$lastElement);
      $this$lastElement.add(value);
   }   
}


public final class MainKt {
    
    
   public static final void main(@NotNull String[] args) {
    
    
      List list = CollectionsKt.mutableListOf(new String[]{
    
    "1", "2", "3"});
      String var2 = ExtensionKt.getLastElement(list);
      System.out.println(var2);
      ExtensionKt.setLastElement(list, "4556");
      var2 = ExtensionKt.getLastElement(list);
      System.out.print(var2);
   }
}

根据上面的代码逻辑就比较清晰了,扩展属性也只是提供了静态方法进行属性的操作,var就会有getXXX/setXXX静态方法,val就只有getXXX静态方法。


伴生对象的扩展

如果一个类定义了伴生对象,我们也可以为伴生对象定义扩展函数和属性。和伴生对象的常规成员一样,可以只使用类名作为限定符来调用伴生对象的扩展成员。

class Example {
    
    
    companion object {
    
    
        fun printInfo() {
    
    
            print("printInfo")
        }
    }
}

//伴生对象扩展函数
fun Example.Companion.printInfo() {
    
    
    println("Example.Companion.printInfo")
}
fun Example.Companion.printExtensionInfo() {
    
    
    println("printExtensionInfo")
}

//调用
fun main(args: Array<String>) {
    
    
    Example.printInfo()  //会打印printInfo, 原因:成员函数优先级大于扩展函数优先级
    Example.printExtensionInfo()   //会打印printExtensionInfo
}

扩展声明为成员(在类里面声明扩展)

在一个类内部可以为另外一个类声明扩展。如下所示:

/** 扩展接收者 */
class Host(val hostname: String) {
    
    
    fun printHostname() {
    
    
        println("Host##printHostname")
    }
}


/** 分发接收者 */
class Connection(val host: Host, val port: Int) {
    
    
    fun printPort(){
    
    
        print(port)
    }

    fun printHostname() {
    
    
        println("Connection##printHostname")
    }

    //扩展函数
    fun Host.printConnectionInfo() {
    
    
        /**
         * Host类中存在函数方法:printHostname
         * Connection类中也存在函数方法:printHostname
         * 从打印结果可看出,扩展函数接收者(Host) 优先级高于 分发接收者(Connection)
         */
        printHostname()

        //强制调用Connection中这个函数方法
        this@Connection.printHostname()
    }

    fun connect() {
    
    
        host.printConnectionInfo()
        //打印:
        Host##printHostname
        Connection##printHostname
    }
}

备注: 针对分发接收者(Connection)与扩展接收者(Host)的成员名字冲突的情况,扩展接收者(Host)优先。要引用分发接收者(Connection)的成员可以使用:this@Connection.函数名或者属性名 ,代码如上面所示。


上一篇:Kotlin学习历程——抽象类与接口
下一篇:Kotlin学习历程——泛型

猜你喜欢

转载自blog.csdn.net/hepingdev/article/details/124269602