Kotlin 扩展函数/属性
在kotlin中,可以扩展一个无法修改代码的类的功能。比如经常使用的三方SDK,我们无法修改SDK的源码,但是可以通过扩展的方式来添加一些函数,扩展SDK的功能
- 扩展函数
用法:
- 定义两个类
/*kotlin中,编译器在编译的时候,默认会给class加上final,
所以要实现可继承,需要加open关键字*/
open class FatherClass {
}
class ChildClass : FatherClass() {
}
- 给这两个类添加扩展函数
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()} ")
}
- 在main方法中执行两个类的扩展函数
FatherClass().printClassName(FatherClass())
FatherClass().printClassName(ChildClass())
ChildClass().printClassName(FatherClass())
ChildClass().printClassName(ChildClass())
ChildClass().printChildClassName(ChildClass())
- 打印输出的值
fatherClass father
fatherClass father
fatherClass father
fatherClass father
childClass child
Process finished with exit code 0
- 我们会发现,不管是父类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());
}
}
- 从java代码片段中可以看出,我们在main方法中调用的只是当前调用类TestMainKt中的静态方法,而且当我们传入ChildClass的时候,被强转成了 FatherClass。这就是为什么不管父类还是子类,调用父类的扩展函数的时候,打印的都是father
- 那么在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));
}
- 可以看出,Java中调用扩展函数的时候,是通过声明了扩展函数的类调用的。而且是直接通过点函数调的。要注意的一点是,在Java中调用类的扩展函数,第一个参数是需要传入要扩展的类的对象的
- 扩展属性
和扩展函数类似,kotlin中也有扩展属性,由于类的扩展并没有将实际的属性或者成员变量插入到类中,所以扩展属性无法初始化赋值,只能通过get/set的方式修改属性的值
- 其他知识点:
- 伴生对象也可以扩展,通过Class.Companion.funName(){}的方式声明
- 在不同包下使用扩展的时候,可以通过import的方式,在调用方导入扩展
参考链接:扩展