在java中咱們須要擴展一個類的新功能時,通常是繼承該類或者使用像裝飾者這樣的設計模式來實現的。
以下:java
public class Animal {
protected String name;
Animal(String name){
this.name = name;
}
public void showName(){
System.out.print(name);
}
}
複製代碼
咱們想要給這個 類加一個吃東西的功能,這時使用Java繼承來實現
實現代碼以下:git
public class Cat extends Animal {
Cat(String name) {
super(name);
}
//新增長吃東西的功能
public void eat(String food){
System.out.print(name+":吃了 "+food);
}
}
複製代碼
這樣咱們就實現了 吃東西的功能。
而在kotlin咱們不用這樣實現了,能夠經過叫作 ++擴展++ 的特殊聲明來完成。Kotlin 支持 ++擴展函數++ 和 ++擴展屬性++。github
聲明一個擴展函數咱們須要用一個 被擴展的類來做爲它的前綴。
公式以下:設計模式
fun 被擴展類名.擴展函數名( 參數 ){app
//實現代碼函數
}ui
咱們來實現上面java實現的功能,代碼以下:this
fun Animal.eat(food:String){
print("$name 吃了 $food")
}
複製代碼
上面kotlin代碼就實現了咱們用java繼承實現的新功能,那要怎麼調用呢 咱們能夠在kotlin中像調用普通的函數來調用這個擴展函數,代碼以下:spa
val animal = Animal("cat")
animal.eat("apple")
複製代碼
上面介紹了kotlin中怎麼調用擴展函數,那在Java中怎麼調用了,代碼以下:設計
Animal animal = new Animal("cat");
//java 中調用kotlin 擴展函數 Aaa 爲擴展函數文件名
AaaKt.eat(animal,"apple");
複製代碼
這樣咱們就在不用繼承該類的狀況下增長了 吃東西的新功能。
注意: 咱們能夠從上面的java調用中能夠看出,擴展並非真正的修改了被擴展類。而只是在kotlin中的調用像是修改了被擴展類。
在kotlin中擴展是靜態分發的,不是根據接收者類型的虛方法。也就是說調用擴展函數是由調用所在的表達式類型來決定的,而不是由表達式運行時求值結果決定的。例如:
//擴展了 Animal 和Cat Cat 繼承 Animal
fun Animal.getFood() = "蘋果"
fun Cat.getFood() = "貓糧"
//在這個方法中 傳入類型是 Animal 因此獲取到的是"蘋果",無論是傳入的是Animal 仍是 Animal 的子類 如Cat
fun printFood(food:Animal){
print(food.getFood())
}
fun main(args: Array<String>) {
val cat = Cat("cat")
//如我在這傳入的是 Cat 可是 輸出出來的仍是 蘋果
printFood(cat)
}
複製代碼
在這個例子會輸出「蘋果」,由於咱們在調用擴展函數時只取決於參數 food 的聲明類型,該類型是 Animal 類。
若是擴展函數和被擴展類中的成員函數有相同的接收類型、名字和參數,那麼這種狀況下 ** 老是取成員**。例如:
class Dog {
fun showName(){
print("Dog")
}
}
fun Dog.showName(){
print("Cat")
}
fun main(args: Array<String>) {
Dog().showName()
}
複製代碼
若是咱們調用 Dog 類的 showName(),它將輸出「Dog」,而不是「Cat」.
這樣是否是很不方便,因此擴展函數能夠重載相同名字可是不一樣簽名成員函數。例如:
class Dog {
fun showName(){
print("Dog")
}
}
fun Dog.showName(name:String){
print("Cat")
}
fun main(args: Array<String>) {
Dog().showName("")
}
複製代碼
這樣咱們調用 showName("")函數,就會輸出 「Cat」了。
擴展函數能夠爲可空的接收者類型定義擴展。這樣的擴展能夠在對象變量上調用,即便其值爲 null,而且能夠在函數體內檢測 this == null ,這樣就能夠在沒有檢測 null 的時候調用成員函數了,列如:
fun Dog?.toString():String{
if (this == null) return "null \n"
return toString()
}
fun main(args: Array<String>) {
var dog:Dog? = null
print(dog.toString())
dog = Dog()
print(dog.toString())
}
複製代碼
這段代碼 第一次打印輸出的是 "null \n" 第二次輸入的是改對象的地址。
與函數相似,Kotlin也支持擴展屬性。 例如:
class Snake{
var aaa = 1
}
var Snake.size:Int
set(value) {aaa = value}
get() = aaa +1
fun main(args: Array<String>) {
val snake = Snake()
print(snake.size)
snake.size = 3
print(snake.size)
}
複製代碼
如上例子所示。
**注意:**因爲擴展是沒有實際將變量成員插入類中,所以對擴展屬性來講幕後字段是無效的。因此擴展屬性不能有初始化器,只能顯示的提供 getters/setters 定義。
例如:
//錯誤:擴展屬性不能有初始化
val Snake.bbb = 1
複製代碼
如上例子是錯誤的。
咱們知道在kotlin中是沒有 static 這個關鍵字的,那咱們要怎麼實現靜態變量呢,那就用到了伴生對象,例如:
class Snake{
var aaa = 1
companion object {
var Bbb = 1
}
}
fun main(args: Array<String>) {
//這樣就達到了Java中靜態變量同樣的效果了
Snake.Bbb
}
複製代碼
那咱們怎麼對伴生對象進行擴展呢,其實也很簡單只是比其餘擴展中間加了一個 Companion 。如例:
fun Snake.Companion.foo(){...}
複製代碼
如上例,我就就定義了一個名爲 foo() 的擴展函數,那咱們這調用這個擴展函數了,其實和普通的擴展函數調用方法是同樣的,以下例:
Snake.foo()
複製代碼