轉(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ò)展方法。