Kotlin学习系列——伴生对象(对象声明的一种特殊情况)

上一节中说到了对象声明,这次我们聊聊对象声明的一种特殊情况,对象声明在一个类内部,也就是标题所说的伴生对象。

伴生对象最简单直白的理解就是与一个类相伴而生的对象,由于它处于类内部,所以必然和包含它的类存在某种联系。

基本用法

伴生对象使用关键字companion来声明,看起来就像是在object关键字前面加的一个修饰符。它的一般格式如下:

class 外部类名{
  companion object 伴生对象名{
      //属性
      //方法
  }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

简单的代码示例如下:

class A{
    companion object {
        fun test(){
            println("Companion Object...")
        }
    }
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

你会发现上面的代码中没有伴生对象名,因为它是可以省略的。下面就依次看看伴生对象都用在那些地方。

弥补static关键字缺憾

在Java中有static关键字表示静态成属性和方法,但在Kotlin中没有static关键字,所以伴生对象和顶层函数一起来弥补了这一缺憾,还是看上面给出的第一个例子,那我们怎么去调用伴生对象中的方法呢。

上面的代码连伴生对象的名字都省略了,所以我们只能通过外部类来调用。格式如下:

外部类.伴生对象内部方法
 
 
  • 1

如下所示:

fun main(args: Array<String>) {
    A.test()
}
 
 
  • 1
  • 2
  • 3

当然对于指定了伴生对象名的情况,调用格式如下:

外部类.伴生对象名.伴生对象内部方法
 
 
  • 1

代码示例如下:

class B{
    companion object KB{
        fun testB(){
            println("Companion Object...")
        }
    }
}

fun main(args: Array<String>) {
    B.testB()
    B.KB.testB()
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

当然你会发现,你在调用时仍然可以省略掉伴生对象名,就像Java中通过类名直接调用静态方法一样。
当然,对于在声明时省略了伴生对象名的情况,Kotlin也提供了一个叫Companion的默认名称,所以我们也可以像下面的方式进行调用:

class A{
    companion object {
        fun test(){
            println("Companion Object...")
        }
    }
}

fun main(args: Array<String>) {
    A.test()
    //默认名称
    A.Companion.test()
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

其实伴生对象的出现也是为了弥补顶层函数的不足,顶层函数不能访问一个类中的private成员,而伴生对象是可以访问的,如下面代码所示:


class C{
    private val age: Int = 0;

    companion object {
        fun out(){

            //伴生对象中可以调用外部的私有成员
            println(C().age)
        }
    }
}

fun main(args: Array<String>) {

    //顶层函数不能调用外部类的私有成员
    println(C().age) //报错
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
实现接口

伴生对象在声明的时候可以像其他类那样实现接口,并实现接口中的方法,如下面的代码所示:

class People(val name: String){

    //伴生对象实现接口
    companion object : Log{
        //实现接口的方法
        override fun printLog(msg: String) {
            println("msg ------- $msg")
        }
    }
}

//该方法的参数是接口实例
fun callLog(log: Log){
    log.printLog("msg")
}

fun main(args: Array<String>) {
    //传入实现接口的伴生对象的类
    callLog(People)
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

上面的代码可以这样理解,伴生对象实现了接口,伴生对象又与外部类存在关联,所以相当于外部类也实现了接口,所以我们可直接传入外部类的名称,这里的这个名称就指代了那个伴生对象。

伴生对象扩展

在前面的内容中我们也说过方法扩展,那这里我们的伴生对象也可以进行扩展,我们可以在其上扩展我们自己的方法。这里所谓的扩展就是在声明伴生对象的时候没有指定函数,而是在类的外部来给伴生对象增加方法,来看看下面的代码示例:

class Person(val name: String){
    companion object {
        //伴生对象内部没有方法
    }
}

//伴生对象扩展方法
fun Person.Companion.toJson(person: Person): String{
    //具体实现
    return "";
}

fun main(args: Array<String>) {
    //调用方法
    val str = Person.toJson(Person("小白"))
}
 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

上面的扩展方法和函数的扩展差别不大,主要是要访问到扩展对象再在其上扩展方法。

写在最后

这一节的伴生对象,总觉得还是有点绕,而且使用场景比较多,容易出错,后续待理解更加深入了会再写文章介绍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值