Kotlin中的object关键字初识

Kotlin中的object关键字

有时候,实现某个功能时,需要对某个类进行一些小的改动,然而完整地定义一个子类去继承往往显得过于繁琐,因为我们可能只想用那么一次,这个时候就可以用到object关键字

主要包括以下三种用法:

  • 对象表达式
  • 对象声明
  • 伴生对象

1. 对象表达式

首先,object可以直接用的

val something = object {
        val name = "Jack"
        val age = 10
        override fun toString(): String {
            return "name=$name age=$age"
        }
    }

这里的object后面直接是一个代码块,这代表着一个类的对象,从外观上看,它没有继承任何类
但是,实际上它是Any的子类,因此这里的toString()是覆写了原来AnytoString()
在这里插入图片描述
因此还可以看到其他可以覆写的Any类的方法
这里使用object定义的同时进行了实例化,尽管该类没有名字,但是可以借助something变量指向,像其他类一样使用

当然在多数情况下,我们不直接继承Any,往往我们需要继承某个具有特定功能的类或者是实现某些接口,用来组合业务需求

open class MyClickListener {

    open fun onClick() {
        println("click...")
    }
}

比如说,我们需要基于这个类的点击功能,自定义满足某个特殊需求的功能,这个时候我们一般需要继承,然后覆写这个点击的功能,然而这个实现可能只会被用到一次,这在Android开发中很常见

val listener = object : MyClickListener() {
        override fun onClick() {
            println("click me...")
        }
    }

右边的内容就是经常能看到的点击事件的形式,object可以把它看成一个占位的,它代表一个类,它的特点是继承了MyClickListener,我们可以通过覆写指定的方法完成自己的逻辑,而右边这个匿名的类的结构是透明的,整个右边同样是包含了定义和实例化
object描述了与父类的继承关系,这种地位可能比较卑微,就像是有血缘关系但是没有名分

随着项目业务的扩展,往往继承的功能也就越多,这个时候可能需要继承的内容就不止一个类

interface NetChangeListener {
    fun onChanged(hasNet: Boolean)
}

再增加一个接口

val listener = object : MyClickListener(), NetChangeListener {
        override fun onClick() {
            println("click me...")
        }

        override fun onChanged(hasNet: Boolean) {
            // todo
        }
    }

定义的时候可以用逗号隔开,需要覆写的方法可以统一放到代码块当中,因为这正是我们需要用到的方法集

2. 访问

将匿名表达式用作函数的返回值,这个时候自然就是定义的同时完成实例化
此时,有一些规则:

  • 一个匿名对象如果是一个局部或private的类型,并且为非内联的,那么这个匿名对象代指的内容的成员,可以通过当前的这个途径获取到
class C {
    private fun getObject() = object {   // private类型的接收,这里的类型会为匿名类型
        val x: String = "x"    // 匿名类的成员
    }

    fun printX() {
        println(getObject().x)    // 满足条件,可以访问
    }
}

在这里插入图片描述
这里的类型是作为Any类的一个匿名子类就是它本身,因此可以轻松拿到其成员

  • publicprivate inline的场景中,真实的类型判断如下:
    • 匿名对象没有声明父类的情况下,判断为Any
    • 声明父类的取父类的类型
interface A {
    fun funFromA() {}
}
interface B

class C {
	// 返回Any
    fun getObject() = object {   // public 向上推父级,Any
        val x: String = "x"
    }

    // 返回父类A
    fun getObjectA() = object: A {   // 能见光的是A,object是私生子
        override fun funFromA() {}
        val x: String = "x"
    }

    // 多个父级,需要指定
    fun getObjectB(): B = object: A, B { // B类型
        override fun funFromA() {}
        val x: String = "x"
    }
}

object如同只是一个小圈子里(private)的称谓(匿名对象),但是在外面(public)仍然需要更加体面的身份(父级)

3. 对象声明

单例模式在实际运用中还是很常见的,而Kotlin提供了更加便捷的使用方式

object ConfigManager {
    fun getXMLByName() {
        // todo
    }
}

使用object代替class直接就完成了单例类的定义

并且单例类会在第一次访问时初始化,该过程是线程安全

ConfigManager.getXMLByName()

直接使用类名加方法名就可以调用方法了

并且,单例类也可以继承,这与之前的object表达式相似

object AdvancedClickListener : MyClickListener() {
    
    override fun onClick() {
        super.onClick()
    }
}

4. 伴生对象

把刚刚的对象声明放到一个普通的类的内部,再标记上companion,这就成了伴生对象

class WebManager {
    companion object UrlHelper {
        fun getBaseUrl() = "http://www.baidu.com"
    }
}

在具体使用时,可以直接使用类名结合伴生对象的成员

WebManager.getBaseUrl()
WebManager.UrlHelper.getBaseUrl()   // 没有问题,只是通常省略伴生对象的类名

类可以访问到位于自己内部的伴生对象的私有成员

class WebManager {
    
    fun log() {
        println("protocol=$PROTOCOL")   // 可以获取到
    }
    
    companion object UrlHelper {

        private const val PROTOCOL = "http://"    // 私有的伴生对象成员
        
        fun getBaseUrl() = "${PROTOCOL}www.baidu.com"
    }
}

相信伴生对象这样的用法会让人不经意联想到Java中的静态成员,然而这二者并不完全一样
在运行时,这些伴生对象的成员依然是属于实例而非类

如果想要生成真正的基于JVM的静态成员,可以针对于字段和方法分别应用@JvmField@JvmStatic

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值