Kotlin 中的伴生对象和静态成员

转载请注明出处:https://www.jianshu.com/p/e6883f85a1bc
本文出自Shawpoo的简书
我的博客:CSDN博客

一、前言

最近公司开发的项目使用的是 Kotlin,所以不得不学起来 Kotlin 这门语言了,毕竟是 Android 官方的第一开发语言嘛!在平时的开发中,我习惯将启动 Activity 的方法以静态方法的形式定义在目标 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);
}

这样写的好处就是方便调用者调用,调用者可以清晰的知道目标 Activity 需要什么参数,而且不用关心传参的 key。但是在 Kotlin 中并不没有 static 这个关键字,该如何处理呢?这里需要用到 Kotlin 的伴生对象来处理。所以本文主要介绍 Kotlin 中伴生对象的应用。

二、伴生对象

1、声明:

Kotlin 中,在类中定义的对象(object)声明,可使用 companion 修饰,这样此对象(object)就是伴生对象了。如下例子:

class NumberTest {
    companion object Obj {  
        var flag = false

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

在本例中,类 NumberTest 中就声明了一个伴生对象,包含属性 flag 和 方法 plus()。但是一个类(class)中最多只能定义一个伴生对象。

2、调用

使用 companion 关键字修改的对象之后,伴生对象就相当于是外部类的对象,我们可以使用类直接调用,如下:

fun main(args: Array<String>) {

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

}

从上面的代码可以看出调用的时候与伴生对象的名称没有多大关系,所以名称 Obj 可以省略不写。

3、伴生对象的作用

通过上面的 NumberTest.plus(1, 2)NumberTest.flag 代码不难看出来,类似于 Java 中使用类访问静态成员的语法。因为 Kotlin 取消了 static 关键字,所以 Kotlin 引入伴生对象来弥补没有静态成员的不足。可见,伴生对象的主要作用就是为其所在的外部类模拟静态成员。

4、与 Java 代码共存

知道了伴生对象的特点之后,那么我们如何与 Java 代码共存呢?也就是如何在 Java 代码中调用 Kotlin 的伴生对象呢?如下:

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());
    }

}
  • 如果声明伴生对象有名称,则使用:
类名.伴生对象名.方法名()
类名.半生对象名.属性的setter,getter方法

例:NumberTest.Obj.plus(2, 3)
  • 如果声明伴生对象无名称,则采用 Companion 关键字调用:
类名.Companion.方法名()
类名.Companion.属性的setter,getter方法

例:NumberTest.Companion.getFlag()

5、@JvmField 和 @JvmStatic 的使用

在上面的例子中,我们知道了可以在 Java 代码中调用 Kotlin 中伴生对象的成员,类似于 Java 类中的静态成员。但是看上去和 Java 中的还是略有区别,因为类名和方法名/属性setter,getter方法名之间多了个伴生对象的名称或者 Companion 关键字。如何使其在调用的时候与 Java 中的调用看上去一样呢?

Kotlin 为我们提供了 @JvmField@JvmStatic 两个注解。@JvmField 使用在属性上,@JvmStatic 使用在方法上。如:

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

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

这样我们在 Java 代码中调用的时候就和 Java 类调用静态成员的形式一致了,Kotlin 代码调用方式不变:

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

6、const 关键字

在伴生对象中,我们可能需要声明一个常量,目的是等同于 Java 中的静态常量。有两种方式,一种是上面所提到的使用 @JvmField 注解,另一种则是使用 const 关键字修饰。这两种声明方式都等同于 Java 中 static final 所修饰的变量。如下代码:

companion object {
      const val m = 2

      @JvmField
      val n = 3
}

// java 代码中调用:
System.out.println(NumberTest.m);
System.out.println(NumberTest.n);

如果不使用 const 修饰的常量,我们需要引用伴生对象来调用,而且必须调用 getter 方法。

companion object {
      const val k = 2
}

// java 代码中调用:
System.out.println(NumberTest.Companion.getK());

而以上 const 关键字使用的影响只是在 Java 中调用方式不同,在 Kotlin 中并无影响。

三、伴生对象的扩展

如果了解 Kotlin 的话,应该知道在 Kotlin 中,对象时可以被扩展的。在 Kotlin 中,如果类中包含伴生对象,则 Kotlin 允许伴生对象扩展方法和属性。也就是为伴生对象所在的外部类扩展静态成员,访问方式一致。

接着上面的列子,为上面的 NumberTest 类扩展一个 minus 方法:

1、扩展方法

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

通过例子我们看出,我们可以通过类名去扩展方法,如果伴生对象有名称的话,使用 类名.伴生对象名.方法名()来扩展,否则使用 类名.Companion.方法名()来扩展即可。

2、扩展属性

var NumberTest.Companion.number
    get() = 3
    set(value) {
        // set 方法并没有 field 可以用来存储 value
        this.plus(value, 2)
    }

val NumberTest.Companion.str
    get() = "这是一个扩展属性"

同样,我们也可以扩展属性,但是扩展属性有以下几个特点:

  • 扩展属性不能有初始值,没有 field 来存储属性值;
  • 扩展属性因为没字段来存储值,所以为计算属性;
  • 扩展 var 属性必须提供 setter 和 getter 方法,扩展 val 属性必须提供 getter 属性。

四、总结

先来个小插曲,回归到本文前言,定义在 Activity 中的启动方法,用到伴生对象可以写成如下格式:

 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)
        }
    }
  • 每个类可以最多有一个半生对象;
  • 伴生对象的成员类似于 Java 的静态成员;
  • 使用 const 关键字修饰常量,类似于 Java 中的 static final修饰。
  • 可以使用 @JvmField 和 @JvmStatic 类似于 Java 中调用静态属性和静态方法;
  • 伴生对象可以扩展属性和扩展方法。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270