Kotlin语法基础八:object关键字

前言

在上一篇文章我们详细的介绍了Kotlin中接口的使用,本篇文章我们继续讲解Kotlin中的基础知识object关键字。有时候,我们需要创建一个对某个类做了轻微改动的类的对象,而不用为之显式声明新的子类。 Kotlin 用对象表达式对象声明处理这种情况。下面我们开始本篇文章的学习~

1.对象表达式

在Java中我们创建一个匿名的对象使用关键字new,而在Kotlin中创建一个匿名的对象我们使用关键字object。下面我们来看一个比较常见的例子,我们给一个View设置点击事件,在Java中我们就会这么写:

less 复制代码
view.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        
    }
});

而在Kolin中我们使用关键字object来声明一个匿名类,并在该关键字后紧跟着冒号,冒号代表着继承,不可省略。

kotlin 复制代码
view.setOnClickListener(object : View.OnClickListener {
    override fun onClick(v: View?) {
    }
})

当然OnClickListener接口是一个函数式接口,通常我们都会用Lambda表达式的方式来实现,关于函数式接口我们已经在上一篇接口的文章中详细的介绍了,这里不再赘述。如果父类中有构造函数,则必须传递相应的构造函数参数给它,和普通的类继承一样多个父类使用逗号隔开。如下代码示例:

疑?我们在继承Student类的时候编译器却给了错误提示,在类与继承的文章中我们已经介绍了Kotlin中的类默认是final的是不可被继承的,不管你使用是什么方式来继承一个类,规则还是要遵守的,类Student必须声明为open的它才能被继承。

任何时候,如果我们只需要一个对象,并不需要特殊的超类型,我们可以这么写:

kotlin 复制代码
private val point = object { 
    val x = 100
    val y = 100
}

fun main() {
    println("x = ${point.x}, y = ${point.y}")
}

// 输出
x = 100, y = 100

这里需要注意的是,匿名对象可以用作只在本地和私有作用域中声明的类型。如果你使用匿名对象作为公有函数的返回类型或者用作公有属性的类型,那么该函数或属性的实际类型会是匿名对象声明的超类型,如果你没有声明任何超类型,就会是 Any。在匿名对象中添加的成员将无法访问。

kotlin 复制代码
val point = object {  // point是一个Any类型
    val x = 100
    val y = 100
}

fun main() {
   val x = point.x // 错误无法访问属性x
}

对象表达式中的代码可以访问来自包含它的外部作用域的变量

ini 复制代码
class View {
    
    private val name = "View"
    
    private val point = object {
        val x = 100
        val y = 100
        val parentName: String = name
    }
    
}

2.对象声明

在Kotlin中我们在object关键字后跟上一个名称,我们就称之为对象声明。在Android Studio中选中当前项目,右击鼠标,New -> Kotlin Class/File,在下图的弹窗中我们选择object:

创建一个名为Controller的对象。如下示例代码:

kotlin 复制代码
object Controller {

    const val TAG = "Controller"

    fun getInfo() { println("getInfo") }

}

就像变量声明一样,对象声明不是一个表达式,不能用在赋值语句的右边。如果我们想要访问该对象中的属性或者方法,可以直接使用ClassName.propertyName或者ClassName.funName。在实际开发中我们常说使用object声明的对象是单例类。那么这个单例类到底是如何实现的呢?在Android Studio中依次打开Tools -> Kotlin -> Show Kotlin Bytecode,在右边的弹窗中我们点击Decompile按钮 ,我们来看一下上述代码反编译成的Java代码:

由上图中标记的2处和3处我们可以看到,使用object声明的对象在反编译成Java代码时也只是一个普通的class类,只是Kotlin编译器帮我们做了一些额外的工作,在该类的static代码块中帮我们创建一个Controller对象,并将该对象的引用赋值给了类变量INSTANCE。当我们再去访问该对象的属性或者方法,事实上就是通过INSTANCE变量来访问的。比如我们在Kotlin中通过Controller.getInfo()访问该对象的getInfo方法,反编译成Java代码就是:

scss 复制代码
Controller.INSTANCE.getInfo()

这些对象也可以有自己的超类型,例如我们使用Controller对象去实现一个接口:

kotlin 复制代码
interface Info {
    fun getInfo()
}

object Controller : Info {
    override fun getInfo() {
      //  TODO("Not yet implemented")
    }
}

对象声明不能在局部作用域(即直接嵌套在函数内部),但是它们可以嵌套到其他对象声明或非内部类中。如下代码示例:

kotlin 复制代码
object DoMain {
    const val TAG = "DoMain"
    object SubDoMain {
        const val TAG = "SubDoMain"
    }
}

3.伴生对象

我们使用companion关键字来标记类内部的对象声明:

kotlin 复制代码
class Person private constructor(){

    companion object Factory {
        const val TAG = "Person"
        fun newInstance() = Person()
    }

}

可以直接只用外部的类名来访问该伴生对象的属性或者方法:

kotlin 复制代码
fun main() {
    Person.TAG
    val instance = Person.newInstance()
}

通常我们都会省略该伴生对象的名称:

kotlin 复制代码
class Person private constructor(){

    companion object {
        const val TAG = "Person"
        fun newInstance() = Person()
    }

}

该伴生对象将会拥有一个默认的名称Compaion:

kotlin 复制代码
fun main() {
    val tag = Person.Companion.TAG
    val instance = Person.Companion.newInstance()
}

在Android Studio中依次打开Tools -> Kotlin -> Show Kotlin ByteCode,在右边的弹出框中我们点击Decompile按钮:

我们来看一下反编译成Java的代码,伴生对象是如何实现的呢?

由上图中标记的2处我们可以看到伴生对象其实就是在该类内部定义了一个静态内部类Companion,也就是该伴生对象的默认类名。我们使用上图中标记的1处也就是该静态内部类的对象实例Companion来访问其内部的方法和属性。由此我们可以看到伴生对象在运行时他们仍是对象的真实成员,伴生对象也可以实现接口:

kotlin 复制代码
interface Factory<T> {
    fun create() : T
}

class Person {
    
    companion object : Factory<Person> {
        override fun create(): Person = Person()
    }

}

4.对象表达式和对象声明之间的差异:

  • 对象表达式是在使用他们的地方立即及初始化的
  • 对象声明是在第一次被访问到时延迟初始化的
  • 伴生对象的初始化是在相应的类被加载时,与 Java 静态初始化器的语义相匹配

总结

对象表达式和对象声明在我们实际开发中是必须要掌握的知识。Kotlin中的语法糖太多了,熟练的掌握每一个小知识点我们才能更好的在实际开发中运用它。下篇文章我们将继续讲解Kotlin中的基础知识数据类、密封类、枚举类。我们下期再见~

相关推荐
耶啵奶膘25 分钟前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^2 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie2 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic3 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
数据猎手小k3 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
有梦想的刺儿3 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具4 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
你的小104 小时前
JavaWeb项目-----博客系统
android
风和先行4 小时前
adb 命令查看设备存储占用情况
android·adb
qq_390161774 小时前
防抖函数--应用场景及示例
前端·javascript