继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

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

jeck猫
关注TA
已关注
手记 460
粉丝 75
获赞 402

一、前言

最近公司开发的项目使用的是 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 中调用静态属性和静态方法;

  • 伴生对象可以扩展属性和扩展方法。

原文链接:http://www.apkbus.com/blog-872153-77955.html


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP