Kotlin基础语法 十五、扩展

Kotlin 能够扩展一个类的新功能而无需继承该类或者使用像装饰器这样的设计模式。 这是通过叫做 扩展 的特殊声明完成。
例如,当你遇到一个不能修改的、来自第三方库中的类,而你又想在不改变这个类的情况下使用极低的成本去扩展这个类的功能,此时你就需要用到扩展,你可以使用扩展为这个类增加函数、增加属性,增加的函数属性就像这个类本来拥有的一样,可以普通的调用。
扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响。

1.扩展函数

扩展函数可以在已有类中添加新的方法,不会对原类做修改。
声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。
声明格式:
fun 被扩展类.扩展函数名(参数列表){
函数体
}
示例如下:我们定义了一个Person类,只有一个userName属性,我们给这个类扩展一个打印用户名的方法。

open class Person(val userName : String) 
private fun Person.printUserName() {
    
    
    System.out.println("用户名:${
      
      this.userName}")
}
val teacher = Person("刘老师")
teacher.printUserName()

注意:

  1. 扩展函数里使用 this 关键字,this在扩展函数内部对应到接收者对象(传过来的在点符号前的对象)
  2. 扩展是静态解析的
    扩展不能真正的修改他们所扩展的类。通过定义一个扩展,你并没有在一个类中插入新成员, 仅仅是可以通过该类型的变量用点表达式去调用这个新函数。
    注意扩展函数是静态分发的, 这意味着调用的扩展函数是由函数调用所在的表达式的类型来决定的, 而不是由表达式运行时求值结果决定的。
    例如下面的例子,我们给Person类和Person类的子类Teacher,都扩展了一个getOccupation()方法返回职业名称,printOccupation方法参数需要一个Person对象,我们传入一个Teacher对象,打印结果是什么呢:
private fun Person.getOccupation() : String {
    
    
    return "无业游民"
}
class Teacher(name : String) : Person(name)
private fun Teacher.getOccupation() : String {
    
    
    return "老师"
}

private fun printOccupation(person : Person) {
    
    
    System.out.println(person.getOccupation())
}
val teacher = Teacher("刘老师")
printOccupation(teacher)

这里的打印结果可能和你想的不一样,打印结果是 “无业游民"而不是"老师”,扩展函数的调用时根据类型来的,这里的类型是Person,那就调用的Person的扩展函数,而不是Person的子类Teacher的扩展函数。当然我们在日常的使用中几乎很少遇到这种情况。

  1. )如果一个类定义了一个成员函数与一个扩展函数,而这两个函数又有相同的接收者类型、 相同的名字,并且都适用给定的参数,这种情况下成员函数大于扩展函数。 例如:
open class Person(val userName : String) {
    
    
    fun printUserName() {
    
    
        System.out.println("内部打印的用户名:${
      
      this.userName}")
    }
}
private fun Person.printUserName() {
    
    
    System.out.println("用户名:${
      
      this.userName}")
}
val teacher = Person("刘老师")
teacher.printUserName()

调用的是Person类的成员函数

2. 扩展属性

与函数类似,Kotlin 支持扩展属性:
声明格式如下:
var/val 被扩展类.扩展属性名
getter初始化器
setter初始化器

示例:

val Person.userAge : Int
    get() = 18

注意:

  1. 由于扩展没有实际的将成员插入类中,因此对扩展属性来说幕后字段是无效的。这就是为什么扩展属性不能有初始化器。他们的行为只能由显式提供的 getters/setters 定义。
    所以 val Person.userAge : Int=18 是错误的

3.扩展的作用域

通常扩展函数或属性定义在顶级包下:
要使用所定义包之外的一个扩展, 通过import导入扩展的函数名进行使用

4.扩展实战:通过扩展解决Android中最常见的快速点击问题。

var <T : View> T.lastClickTime : Long
    set(value) = setTag(1766613352 , value)
    get() = getTag(1766613352) as? Long ?: 0
fun <T : View> T.singleClick(onClickListener : View.OnClickListener , time : Long = 1500) {
    
    
    setOnClickListener {
    
    
        val currentTimeMillis = System.currentTimeMillis()
        if (currentTimeMillis - lastClickTime > time || this is Checkable) {
    
    
            lastClickTime = currentTimeMillis
            onClickListener.onClick(this)
        }
    }
}

我们给View类扩展了一个属性记录上次的点击时间,扩展了一个方法,判断是否是快速点击,如果不是快速点击则想要点击事件。
可以看到,扩展的强大与便捷。

5 .扩展是怎么实现的?

我们将 4中的例子使用Android Studio 将kotlin代码转换为Java代码,操作步骤:一次点击Tools - Kotlin -Show Kotlin Bytecode - Decompile,我们查看转换的Java代码,如下:

public final class AaKt {
    
    
   public static final long getLastClickTime(@NotNull View $this$lastClickTime) {
    
    
      Intrinsics.checkNotNullParameter($this$lastClickTime, "$this$lastClickTime");
      Object var10000 = $this$lastClickTime.getTag(1766613352);
      if (!(var10000 instanceof Long)) {
    
    
         var10000 = null;
      }

      return (Long)var10000 != null ? (Long)var10000 : 0L;
   }

   public static final void setLastClickTime(@NotNull View $this$lastClickTime, long value) {
    
    
      Intrinsics.checkNotNullParameter($this$lastClickTime, "$this$lastClickTime");
      $this$lastClickTime.setTag(1766613352, value);
   }

   public static final void singleClick(@NotNull final View $this$singleClick, @NotNull final OnClickListener onClickListener, final long time) {
    
    
      Intrinsics.checkNotNullParameter($this$singleClick, "$this$singleClick");
      Intrinsics.checkNotNullParameter(onClickListener, "onClickListener");
      $this$singleClick.setOnClickListener((OnClickListener)(new OnClickListener() {
    
    
         public final void onClick(View it) {
    
    
            long currentTimeMillis = System.currentTimeMillis();
            if (currentTimeMillis - AaKt.getLastClickTime($this$singleClick) > time || $this$singleClick instanceof Checkable) {
    
    
               AaKt.setLastClickTime($this$singleClick, currentTimeMillis);
               onClickListener.onClick($this$singleClick);
            }

         }
      }));
   }

   // $FF: synthetic method
   public static void singleClick$default(View var0, OnClickListener var1, long var2, int var4, Object var5) {
    
    
      if ((var4 & 2) != 0) {
    
    
         var2 = 1500L;
      }

      singleClick(var0, var1, var2);
   }
}

可以看到,扩展变量和扩展方法 都被转变成了静态方法。我们可以理解为:
扩展变量和扩展方法 就是将变量的设置获取、方法 都 转变为静态方法

猜你喜欢

转载自blog.csdn.net/weixin_43864176/article/details/124502980