Kotlin 扩展函数/属性

478 阅读3分钟

Kotlin 扩展函数/属性

在kotlin中,可以扩展一个无法修改代码的类的功能。比如经常使用的三方SDK,我们无法修改SDK的源码,但是可以通过扩展的方式来添加一些函数,扩展SDK的功能

- 扩展函数

用法:

  1. 定义两个类
/*kotlin中,编译器在编译的时候,默认会给class加上final,
所以要实现可继承,需要加open关键字*/
open class FatherClass {

}
class ChildClass : FatherClass() {
}
  1. 给这两个类添加扩展函数
fun FatherClass.className() = "father"
fun ChildClass.className() = "child"

fun FatherClass.printClassName(fatherClass: FatherClass) {
    println(" fatherClass  ${fatherClass.className()} ")
}

fun ChildClass.printChildClassName(childClass: ChildClass){
    println(" childClass  ${childClass.className()} ")
}
  1. 在main方法中执行两个类的扩展函数
    FatherClass().printClassName(FatherClass())
    FatherClass().printClassName(ChildClass())
    ChildClass().printClassName(FatherClass())
    ChildClass().printClassName(ChildClass())

    ChildClass().printChildClassName(ChildClass())
  1. 打印输出的值
 fatherClass  father 
 fatherClass  father 
 fatherClass  father 
 fatherClass  father 
 childClass  child 

Process finished with exit code 0
  1. 我们会发现,不管是父类FatherClass还是子类ChildClass,调用父类的扩展函数printClassName,打印的className都是father 而当子类调用给自己定义的扩展函数时,打印的className是child。
    这是因为对于类的扩展函数来说,它无法真正的修改它所扩展的类,它并不是直接给类添加一个函数,只是在声明扩展函数的类中定义了静态方法,然后传入扩展函数的类对象而已。我们看看上述代码编译成java文件是什么样子的
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

public final class TestMainKt {
   @NotNull
   public static final String className(@NotNull FatherClass $this$className) {
      Intrinsics.checkNotNullParameter($this$className, "$this$className");
      return "father";
   }

   @NotNull
   public static final String className(@NotNull ChildClass $this$className) {
      Intrinsics.checkNotNullParameter($this$className, "$this$className");
      return "child";
   }

   public static final void printClassName(@NotNull FatherClass $this$printClassName, @NotNull FatherClass fatherClass) {
      Intrinsics.checkNotNullParameter($this$printClassName, "$this$printClassName");
      Intrinsics.checkNotNullParameter(fatherClass, "fatherClass");
      String var2 = " fatherClass  " + className(fatherClass) + ' ';
      boolean var3 = false;
      System.out.println(var2);
   }

   public static final void printChildClassName(@NotNull ChildClass $this$printChildClassName, @NotNull ChildClass childClass) {
      Intrinsics.checkNotNullParameter($this$printChildClassName, "$this$printChildClassName");
      Intrinsics.checkNotNullParameter(childClass, "childClass");
      String var2 = " childClass  " + className(childClass) + ' ';
      boolean var3 = false;
      System.out.println(var2);
   }

   public static final void main(@NotNull String[] args) {
      Intrinsics.checkNotNullParameter(args, "args");
      printClassName(new FatherClass(), new FatherClass());
      printClassName(new FatherClass(), (FatherClass)(new ChildClass()));
      printClassName((FatherClass)(new ChildClass()), new FatherClass());
      printClassName((FatherClass)(new ChildClass()), (FatherClass)(new ChildClass()));
      printChildClassName(new ChildClass(), new ChildClass());
   }
}

  1. 从java代码片段中可以看出,我们在main方法中调用的只是当前调用类TestMainKt中的静态方法,而且当我们传入ChildClass的时候,被强转成了 FatherClass。这就是为什么不管父类还是子类,调用父类的扩展函数的时候,打印的都是father
  2. 那么在Java中怎么调用kotlin的扩展函数呢,看下面的代码
//我们定义一个系统类File的扩展函数
fun File.printReadText(text: String) {
    println("  printReadText \n $text")
}

在kotlin的main函数中这样调
fun main(args: Array<String>) {
    val file = File("IdeaProjects.iml")
    file.printReadText(file.readText())
}
在Java的main函数中这样调
    public static void main(String[] args) {
        File file = new File("IdeaProjects.iml");
        TestMainKt.printReadText(file, FilesKt.readText(file, Charsets.UTF_8));
    }
  1. 可以看出,Java中调用扩展函数的时候,是通过声明了扩展函数的类调用的。而且是直接通过点函数调的。要注意的一点是,在Java中调用类的扩展函数,第一个参数是需要传入要扩展的类的对象的

- 扩展属性

和扩展函数类似,kotlin中也有扩展属性,由于类的扩展并没有将实际的属性或者成员变量插入到类中,所以扩展属性无法初始化赋值,只能通过get/set的方式修改属性的值

- 其他知识点:

  1. 伴生对象也可以扩展,通过Class.Companion.funName(){}的方式声明
  2. 在不同包下使用扩展的时候,可以通过import的方式,在调用方导入扩展

参考链接:扩展