Kotlin知識概括(三) —— 頂層成員與擴展

前序

        在Java項目中,多多少少都存在以Utils結尾的Java類。其內部並沒有任何狀態和實例函數,只有一堆與該名稱相關的靜態屬性或靜態方法。該類只是做爲一種容器存儲着靜態屬性和靜態方法。java

頂層函數

        Kotlin認爲,根本不須要建立這些無心義的類。能夠直接將函數放在代碼文件的頂層,不用附屬於任何一個類。android

在com.daqi包中的daqi.kt文件中定義頂層函數joinToString()bash

package com.daqi

@JvmOverloads
fun <T> joinToString(collection: Collection<T>,
                      separator:String = ",",
                      prefix:String = "",
                      postfix:String = ""):String{
    val result = StringBuilder(prefix)
    for ((index,element) in collection.withIndex()){
        if (index > 0)
            result.append(separator)
        result.append(element)
    }
    result.append(postfix)
    return result.toString()
}
複製代碼

        在Kotlin中,頂層函數屬於包內成員,包內能夠直接使用,包外只須要import該頂層函數,便可使用。app

        Kotlin和Java具備很強互操做性,若是讓Java調用頂層函數該怎麼調用呢?先看一下頂層函數編譯成Java是什麼樣的:函數

public final class DaqiKt {
   @NotNull
   public static final String joinToString(Collection collection, String separator,
                    String prefix,String postfix) {
   }
}
複製代碼

        編譯器將頂層函數所在的文件名daqi.kt做爲類名DaqiKt,生成對應的類文件。該kt文件下的全部頂層函數都編譯爲這個類的靜態函數。post

so,若是在Java中調用Kotlin的頂層函數時,須要對其的文件名轉換爲對應的類名,再進行調用。ui

DaqiKt.joinToString(new ArrayList<>(),"",",","");
複製代碼

        若是想規定kt文件轉換爲Java類時的類名,可使用@file:JvmName()註解進行修改。將其放在文件的開頭,位於包名以前:this

@file:JvmName("StringUtils")
package com.daqi

fun <T> joinToString(...){
	...
}
複製代碼

就可使用特定的類名在Java中調用對應的頂層函數。spa

StringUtils.joinToString(new ArrayList<>(),"",",","");
複製代碼

頂層屬性

        既然有頂層方法,應該也有頂層屬性。和頂層函數同樣,屬性也能夠放在文件的頂層,不附屬與任何一個類。這種屬性叫頂層屬性。.net

@file:JvmName("StringUtils")
package com.daqi

val daqiField :String = "daqi"
複製代碼

        頂層屬性和其餘任意屬性同樣,都提供對應的訪問器(val 變量提供getter,var 變量提供getter 和 setter)。也就是說,當Java訪問該頂層屬性時,經過訪問器進行訪問的。

StringUtils.getDaqiField();
複製代碼

經過反編譯查看其轉換爲Java的樣子:

@NotNull
private static final String daqiField = "daqi";

@NotNull
public static final String getDaqiField() {
    return daqiField;
}
複製代碼

頂層屬性被定義爲私有的靜態對象,並配套了一個靜態訪問器方法。

        若是須要定義public的靜態變量,能夠用const關鍵字修飾該變量。(僅適用於基礎數據類型和String類型的屬性)

在反編譯的文件中能夠看到,靜態屬性變成public,且沒有了具體的靜態訪問器。

//Kotlin
const val daqiField :String = "daqi"

//Java
public static final String daqiField = "daqi";
複製代碼

擴展函數

        Kotlin能夠在無需繼承的狀況下擴展一個類的功能,而後像內部函數同樣直接經過對象進行調用。擴展函數這個特性能夠很平滑與現有Java代碼進行集成。

        聲明一個擴展函數,須要用一個接收者類型也就是被擴展的類型來做爲他的前綴。而調用該擴展函數的對象,叫做接收者對象。接收者對象用this表示,this無關緊要。

fun String.lastChar():Char{
    return this.get(this.length - 1)
}

//調用擴展函數
"daqi".lastChar()
複製代碼

擴展函數的可見性

        在擴展函數中,能夠直接訪問被擴展類的方法和屬性。但擴展函數不容許你打破對象的封裝性,擴展函數不能訪問privateprotected的成員。具體什麼意思呢,先定義一個java類:

public class daqiJava {
    private String str = "";
    public String name = "";

    public void daqi(){
    }

    private void daqi(String name){
    }
}
複製代碼

對其該類進行擴展:

        public的方法能夠正常訪問,但凡用privateprotected修飾的屬性或方法,沒法在擴展函數中被調用。

Java調用擴展函數

        回到Kotlin和Java交互性的問題,Java如何調用擴展函數的呢?這時候又要一波反編譯:

public static final void extensionMethod(daqiJava $receiver,String string) {
    
}
複製代碼

        擴展函數daqiJava#extensionMethod()被轉換爲一個相同名稱的靜態函數。函數第一個參數變成接受者類型,後面纔是原函數的參數列表。也就是說Java調用擴展函數時,須要先傳入對應的接收者對象,再傳入該擴展函數的參數。

擴展是靜態解析的

        在JVM語言的多態中,被重寫方法的調用依據其調用對象的實際類型進行調用。但擴展函數是靜態分發的,即意味着擴展函數是由其所在表達式中的調用者的類型來決定的。

咱們都知道Button是View的子類,同時爲View和Button定義名爲daqi的擴展函數。

val view:View = Button()
view.daqi()
複製代碼

        此時調用的是View的擴展函數,即便它實質是一個Button對象。由於擴展函數所在的表達式中,view是View類型,而不是Button類型。

擴展函數其餘特性

  • 擴展函數與頂層函數相似,在Java層進行調用時,依據其所在的文件名做爲類名,其做爲靜態函數,存儲在該類中。(也支持@file:JvmName("")進行)

  • 在擴展函數中,除了能夠調用接收者類型的成員函數和成員屬性外,還能夠調用該類的擴展函數。

  • 若是一個類的成員函數與擴展函數擁有相同的方法簽名,成員函數會被優先使用。

  • 擴展函數實際上是靜態函數,擴展函數不能被子類重寫。但子類仍能夠調用父類的擴展函數。

擴展屬性

        擴展屬性不能有初始化器,它們的行爲只能由顯式提供的 getters/setters 定義。由於沒有地方對它進行存儲,不可能給現有的Java對象實例添加額外的屬性。只是用屬性的語法對接受者類型進行擴展。

聲明一個擴展常量:

val String.lastChar:Char
	get() = get(length - 1)
複製代碼

聲明一個擴展變量:

var StringBuffer.lastChar:Char
    get() = get(length - 1)
    set(value:Char){
        this.setCharAt(length - 1,value)
    }
複製代碼

總結:

  • 頂層函數和擴展函數均可以去掉以」Utils「結尾的靜態方法容器類。
  • 頂層函數和頂層屬性提供全局的方法和屬性,不須要任何對象實例進行調用。
  • 擴展函數須要接受者對象實例來進行調用。
  • 頂層屬性是等價於私有的靜態對象。
  • 若想獲取基本類型和String類型的公有靜態對象,在頂層屬性定義時,添加const關鍵字。
  • 擴展能夠毫無反作用給原有庫的類增長屬性和方法。

參考資料:

android Kotlin系列:

Kotlin知識概括(一) —— 基礎語法

Kotlin知識概括(二) —— 讓函數更好調用

Kotlin知識概括(三) —— 頂層成員與擴展

Kotlin知識概括(四) —— 接口和類

Kotlin知識概括(五) —— Lambda

Kotlin知識概括(六) —— 類型系統

Kotlin知識概括(七) —— 集合

Kotlin知識概括(八) —— 序列

Kotlin知識概括(九) —— 約定

Kotlin知識概括(十) —— 委託

Kotlin知識概括(十一) —— 高階函數

Kotlin知識概括(十二) —— 泛型

Kotlin知識概括(十三) —— 註解

Kotlin知識概括(十四) —— 反射

相關文章
相關標籤/搜索