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中的基础知识数据类、密封类、枚举类。我们下期再见~

相关推荐
草莓熊Lotso1 小时前
Linux 文件描述符与重定向实战:从原理到 minishell 实现
android·linux·运维·服务器·数据库·c++·人工智能
恋猫de小郭1 小时前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
工程师老罗8 小时前
如何在Android工程中配置NDK版本
android
崔庆才丨静觅8 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅9 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端