Kotlin 中的伴生對(duì)象和靜態(tài)成員

轉(zhuǎn)載請(qǐng)注明出處:http://m.itdecent.cn/p/e6883f85a1bc
本文出自Shawpoo的簡(jiǎn)書
我的博客:CSDN博客

一、前言

最近公司開發(fā)的項(xiàng)目使用的是 Kotlin,所以不得不學(xué)起來(lái) Kotlin 這門語(yǔ)言了,畢竟是 Android 官方的第一開發(fā)語(yǔ)言嘛!在平時(shí)的開發(fā)中,我習(xí)慣將啟動(dòng) Activity 的方法以靜態(tài)方法的形式定義在目標(biāo) Activity 中,如下:

public static final String EXTRA_PARAMS = "extra_params";

public static void open(Context context, String params) {
    Intent intent = new Intent(context, TestActivity.class);
    intent.putExtra(EXTRA_PARAMS, params);
    context.startActivity(intent);
}

這樣寫的好處就是方便調(diào)用者調(diào)用,調(diào)用者可以清晰的知道目標(biāo) Activity 需要什么參數(shù),而且不用關(guān)心傳參的 key。但是在 Kotlin 中并不沒(méi)有 static 這個(gè)關(guān)鍵字,該如何處理呢?這里需要用到 Kotlin 的伴生對(duì)象來(lái)處理。所以本文主要介紹 Kotlin 中伴生對(duì)象的應(yīng)用。

二、伴生對(duì)象

1、聲明:

Kotlin 中,在類中定義的對(duì)象(object)聲明,可使用 companion 修飾,這樣此對(duì)象(object)就是伴生對(duì)象了。如下例子:

class NumberTest {
    companion object Obj {  
        var flag = false

        fun plus(num1: Int, num2: Int): Int {
            return num1 + num2
        }
    }
}

在本例中,類 NumberTest 中就聲明了一個(gè)伴生對(duì)象,包含屬性 flag 和 方法 plus()。但是一個(gè)類(class)中最多只能定義一個(gè)伴生對(duì)象。

2、調(diào)用

使用 companion 關(guān)鍵字修改的對(duì)象之后,伴生對(duì)象就相當(dāng)于是外部類的對(duì)象,我們可以使用類直接調(diào)用,如下:

fun main(args: Array<String>) {

    println(NumberTest.plus(1, 2))  // 3
    println(NumberTest.flag)  // false

}

從上面的代碼可以看出調(diào)用的時(shí)候與伴生對(duì)象的名稱沒(méi)有多大關(guān)系,所以名稱 Obj 可以省略不寫。

3、伴生對(duì)象的作用

通過(guò)上面的 NumberTest.plus(1, 2)NumberTest.flag 代碼不難看出來(lái),類似于 Java 中使用類訪問(wèn)靜態(tài)成員的語(yǔ)法。因?yàn)?Kotlin 取消了 static 關(guān)鍵字,所以 Kotlin 引入伴生對(duì)象來(lái)彌補(bǔ)沒(méi)有靜態(tài)成員的不足??梢姡樯鷮?duì)象的主要作用就是為其所在的外部類模擬靜態(tài)成員。

4、與 Java 代碼共存

知道了伴生對(duì)象的特點(diǎn)之后,那么我們?nèi)绾闻c Java 代碼共存呢?也就是如何在 Java 代碼中調(diào)用 Kotlin 的伴生對(duì)象呢?如下:

public class NumberJavaTest {

    public static void main(String[] args) {
        System.out.println(NumberTest.Obj.plus(2, 3)); // 5
        // System.out.println(NumberTest.Companion.plus(2, 3));
        NumberTest.Obj.setFlag(true);
        // NumberTest.Companion.setFlag(true);
        System.out.println(NumberTest.Obj.getFlag()); // true
        // System.out.println(NumberTest.Companion.getFlag());
    }

}
  • 如果聲明伴生對(duì)象有名稱,則使用:
類名.伴生對(duì)象名.方法名()
類名.半生對(duì)象名.屬性的setter,getter方法

例:NumberTest.Obj.plus(2, 3)
  • 如果聲明伴生對(duì)象無(wú)名稱,則采用 Companion 關(guān)鍵字調(diào)用:
類名.Companion.方法名()
類名.Companion.屬性的setter,getter方法

例:NumberTest.Companion.getFlag()

5、@JvmField 和 @JvmStatic 的使用

在上面的例子中,我們知道了可以在 Java 代碼中調(diào)用 Kotlin 中伴生對(duì)象的成員,類似于 Java 類中的靜態(tài)成員。但是看上去和 Java 中的還是略有區(qū)別,因?yàn)轭惷头椒?屬性setter,getter方法名之間多了個(gè)伴生對(duì)象的名稱或者 Companion 關(guān)鍵字。如何使其在調(diào)用的時(shí)候與 Java 中的調(diào)用看上去一樣呢?

Kotlin 為我們提供了 @JvmField@JvmStatic 兩個(gè)注解。@JvmField 使用在屬性上,@JvmStatic 使用在方法上。如:

class NumberTest {
    companion object {
        @JvmField
        var flag = false

        @JvmStatic
        fun plus(num1: Int, num2: Int): Int {
            return num1 + num2
        }
    }
}

這樣我們?cè)?Java 代碼中調(diào)用的時(shí)候就和 Java 類調(diào)用靜態(tài)成員的形式一致了,Kotlin 代碼調(diào)用方式不變:

 public static void main(String[] args) {
        System.out.println(NumberTest.plus(2, 3));
        NumberTest.flag = true;
        System.out.println(NumberTest.flag);
}

6、const 關(guān)鍵字

在伴生對(duì)象中,我們可能需要聲明一個(gè)常量,目的是等同于 Java 中的靜態(tài)常量。有兩種方式,一種是上面所提到的使用 @JvmField 注解,另一種則是使用 const 關(guān)鍵字修飾。這兩種聲明方式都等同于 Java 中 static final 所修飾的變量。如下代碼:

companion object {
      const val m = 2

      @JvmField
      val n = 3
}

// java 代碼中調(diào)用:
System.out.println(NumberTest.m);
System.out.println(NumberTest.n);

如果不使用 const 修飾的常量,我們需要引用伴生對(duì)象來(lái)調(diào)用,而且必須調(diào)用 getter 方法。

companion object {
      const val k = 2
}

// java 代碼中調(diào)用:
System.out.println(NumberTest.Companion.getK());

而以上 const 關(guān)鍵字使用的影響只是在 Java 中調(diào)用方式不同,在 Kotlin 中并無(wú)影響。

三、伴生對(duì)象的擴(kuò)展

如果了解 Kotlin 的話,應(yīng)該知道在 Kotlin 中,對(duì)象時(shí)可以被擴(kuò)展的。在 Kotlin 中,如果類中包含伴生對(duì)象,則 Kotlin 允許伴生對(duì)象擴(kuò)展方法和屬性。也就是為伴生對(duì)象所在的外部類擴(kuò)展靜態(tài)成員,訪問(wèn)方式一致。

接著上面的列子,為上面的 NumberTest 類擴(kuò)展一個(gè) minus 方法:

1、擴(kuò)展方法

fun NumberTest.Companion.minus(str: String): Int {
    if (str != null && str.isNotEmpty()) {
        return try {
            str.toInt()
        } catch (e: Exception) {
            0
        }
    }
    return 0
}

通過(guò)例子我們看出,我們可以通過(guò)類名去擴(kuò)展方法,如果伴生對(duì)象有名稱的話,使用 類名.伴生對(duì)象名.方法名()來(lái)擴(kuò)展,否則使用 類名.Companion.方法名()來(lái)擴(kuò)展即可。

2、擴(kuò)展屬性

var NumberTest.Companion.number
    get() = 3
    set(value) {
        // set 方法并沒(méi)有 field 可以用來(lái)存儲(chǔ) value
        this.plus(value, 2)
    }

val NumberTest.Companion.str
    get() = "這是一個(gè)擴(kuò)展屬性"

同樣,我們也可以擴(kuò)展屬性,但是擴(kuò)展屬性有以下幾個(gè)特點(diǎn):

  • 擴(kuò)展屬性不能有初始值,沒(méi)有 field 來(lái)存儲(chǔ)屬性值;
  • 擴(kuò)展屬性因?yàn)闆](méi)字段來(lái)存儲(chǔ)值,所以為計(jì)算屬性;
  • 擴(kuò)展 var 屬性必須提供 setter 和 getter 方法,擴(kuò)展 val 屬性必須提供 getter 屬性。

四、總結(jié)

先來(lái)個(gè)小插曲,回歸到本文前言,定義在 Activity 中的啟動(dòng)方法,用到伴生對(duì)象可以寫成如下格式:

 companion object {
        const val EXTRA_PARAMS = "extra_params"

        fun open(context: Context, params: String) {
            var intent = Intent(context, TestActivity::class.java)
            intent.putExtra(EXTRA_PARAMS, params)
            context.startActivity(intent)
        }
    }
  • 每個(gè)類可以最多有一個(gè)半生對(duì)象;
  • 伴生對(duì)象的成員類似于 Java 的靜態(tài)成員;
  • 使用 const 關(guān)鍵字修飾常量,類似于 Java 中的 static final修飾。
  • 可以使用 @JvmField 和 @JvmStatic 類似于 Java 中調(diào)用靜態(tài)屬性和靜態(tài)方法;
  • 伴生對(duì)象可以擴(kuò)展屬性和擴(kuò)展方法。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容