您有过想给某个类的 API 添加新的功能或属性吗?html
一般您能够经过继承该类,或者建立一个新的函数,该函数接收该类的实例做为参数,从而解决这个问题。Java 编程语言一般使用 Utils 类来解决此类问题,但这样的方式并不支持代码自动补全,会让写出的代码比较难以查找,使用起来也不直观。虽然这两种方式均可以解决问题,但终究仍是很难写出简洁易读的代码。编程
值得庆幸的是,Kotlin 带着 扩展函数和属性 来 "拯救" 咱们了。经过它,您无需使用继承,或建立接收类实例的函数便可为某个类添加功能。同 Java 这类编程语言不一样,Android Studio 的自动补全功能是支持 Kotlin 扩展的。扩展能够用于第三方代码库、Android SDK 以及用户自定义的类。编程语言
继续阅读,探索如何经过扩展来提高您的代码可读性。函数
咱们假设您有一个叫作 Dog 的类,它有 name、breed、age 三个属性。工具
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> data class Dog(val name: String, val breed: String, val age: Int)
领养机构但愿扩展 Dog 类,使其具备打印狗狗信息的功能,这样能够方便感兴趣的人来领养。为此咱们实现一个扩展函数,方法同实现一个普通的函数是同样的,除了一点: 您须要在函数名前面加上要扩展的类名以及一个 "." 符号。在函数体中,您可使用 this 来引用接收者对象,在该函数做用域内可以访问到接收者所属类的所有成员对象。this
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> fun Dog.printDogInformation() { println("Meet ${this.name}, a ${this.age} year old ${this.breed}") }
调用 printDogInformation() 方法就同调用其它 Dog 类中的函数同样。编码
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> fun main() { val dog = Dog("Jen", "Pomeranian", 13) dog.printDogInformation() }
从 Java 代码中调用扩展函数code
扩展函数并不属于咱们要扩展的类的一部分,所以当咱们在 Java 语言中尝试调用该方法时,并不能在该类的其它方法中找到它。正如咱们稍后所看到的,扩展会在其被定义的文件中反编译成静态方法,并接收一个咱们要扩展的类的实例做为参数。如下就是在 Java 中调用 printDogInformation() 扩展函数的示例代码。orm
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> DogExtensionKt.printDogInformation(dog);
为 nullable 类型定义扩展函数htm
您也能够为 nullable 类型定义扩展函数。与其在调用扩展函数以前进行 null 检查,咱们能够直接为 nullable 类型定义扩展函数,让扩展函数自己包含对 null 的检查。如下就是为 nullable 类型定义扩展函数 printInformation() 的示例代码。
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> fun Dog?.printInformation() { if (this == null){ println("No dog found") return } println("Meet ${this.name} a ${this.age} year old ${this.breed}") }
您能够发现,调用 printInformation() 函数时并不须要作 null 检查。
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> fun main() { val dog : Dog? = null dog.printInformation() // prints "No dog found" }
做为领养机构,可能还想知道狗狗的年龄是否符合被领养的条件,所以咱们实现了一个名为 isReadyToAdopt 的扩展属性,用于检查狗狗的年龄是否超过 1 岁。
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> val Dog.isReadyToAdopt: Boolean get() = this.age > 1
调用此属性就同调用 Dog 类中的其它属性同样。
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> fun main() { val dog1 = Dog("Jen", "Pomeranian", 13) if(dog1.isReadyToAdopt){ print("${dog1.name} is ready to be adopted") } }
您并不能在扩展函数里复写类中现有的成员函数。若是您所定义的扩展函数同已有的成员函数签名一致,那么只有现有的成员函数会被正常调用,由于函数调用取决于变量声明时的静态类型,而不是存储在该变量中值的运行时类型。例如,您不能在 String 上扩展 toUppercase() 方法,可是您能够扩展一个名为 convertToUppercase() 的方法。
当您扩展了一个不属于您定义的类型,而该类型所在的代码库中存在一个同您的扩展具备相同签名的扩展函数,那么上述所说的这种行为就会显现出后果。在这种状况下,会调用代码库中的扩展函数,而您所获得的惟一信息是您所定义的扩展函数变成了一个未被使用的方法。
咱们能够在 Android Studio 中对 printDogInformation() 反编译,方法是在 Tools/Kotlin/Show Kotlin Bytecode 中点击 Decompile 按钮。如下是反编译 printDogInformation() 以后生成的代码:
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> public static final void printDogInformation(@NotNull Dog $this$printDogInformation) { Intrinsics.checkParameterIsNotNull($this$printDogInformation, "$this$printDogInformation"); String var1 = "Meet " + $this$printDogInformation.getName() + ", a " + $this$printDogInformation.getAge() + " year old " + $this$printDogInformation.getBreed(); boolean var2 = false; System.out.println(var1); }
实际上,扩展函数看起来只是普通的、接收一个类实例做为参数的静态函数,与接收类并无任何其它联系。这就是为何代码没有 Backing Fields 的缘由——它们实际上并无在类中插入任何成员。
总的来讲,扩展是一个颇有用的工具。在使用扩展时需仔细思虑,请牢记如下提示,让您的代码更直观和易读。
提示:
祝您编码愉快!