您有过想给某个类的 API 增加新的性能或属性吗?
通常您能够通过继承该类,或者创立一个新的函数,该函数接管该类的实例作为参数,从而解决这个问题。Java 编程语言通常应用 Utils 类来解决此类问题,但这样的形式并不反对代码主动补全,会让写出的代码比拟难以查找,应用起来也不直观。尽管这两种形式都能够解决问题,但究竟还是很难写出简洁易读的代码。
值得庆幸的是,Kotlin 带着 扩大函数和属性 来 “援救” 咱们了。通过它,您无需应用继承,或创立接管类实例的函数即可为某个类增加性能。同 Java 这类编程语言不同,Android Studio 的主动补全性能是反对 Kotlin 扩大的。扩大能够用于第三方代码库、Android SDK 以及用户自定义的类。
持续浏览,摸索如何通过扩大来晋升您的代码可读性。
扩大函数的应用
咱们假如您有一个叫做 Dog 的类,它有 name、breed、age 三个属性。
<code class="Kotlin"><!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> data class Dog(val name: String, val breed: String, val age: Int)
领养机构心愿扩大 Dog 类,使其具备打印狗狗信息的性能,这样能够不便感兴趣的人来领养。为此咱们实现一个扩大函数,办法同实现一个一般的函数是一样的,除了一点: 您须要在函数名后面加上要扩大的类名以及一个 “.” 符号。在函数体中,您能够应用 this 来援用接收者对象,在该函数作用域内可能拜访到接收者所属类的全副成员对象。
<code class="Kotlin"><!-- 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 类中的函数一样。
<code class="Kotlin"><!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> fun main() { val dog = Dog("Jen", "Pomeranian", 13) dog.printDogInformation() }
从 Java 代码中调用扩大函数
扩大函数并不属于咱们要扩大的类的一部分,因而当咱们在 Java 语言中尝试调用该办法时,并不能在该类的其它办法中找到它。正如咱们稍后所看到的,扩大会在其被定义的文件中反编译成静态方法,并接管一个咱们要扩大的类的实例作为参数。以下就是在 Java 中调用 printDogInformation() 扩大函数的示例代码。
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> DogExtensionKt.printDogInformation(dog);
为 nullable 类型定义扩大函数
您也能够为 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() 之后生成的代码:
<code class="Kotlin"><!-- 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 的起因——它们实际上并没有在类中插入任何成员。
总结
总的来说,扩大是一个很有用的工具。在应用扩大时需认真思虑,请牢记以下提醒,让您的代码更直观和易读。
提醒:
- 扩大是动态散发的;
- 成员函数永远是 “赢家”;
- 领养一只狗狗!
祝您编码欢快!