Kotlin 对象表达式

在 Kotlin 中,‌对象表达式(Object Expression) ‌ 是一种快速创建匿名类实例的语法,类似于 Java 的匿名内部类,但更简洁灵活。它适用于需要临时实现接口或继承类的场景。


一、核心特性

  1. ‌匿名性 ‌
    对象表达式没有显式类名,直接通过 object 关键字声明。
  2. ‌临时性 ‌
    通常在局部作用域内使用,无法在其他地方复用。
  3. ‌捕获外部变量 ‌
    可以访问并修改其作用域内的变量(与 Java 匿名内部类不同,‌无 final 限制‌)。

二、基本语法

1. 实现单个接口

kotlin 复制代码
// 定义一个接口
interface ClickListener {
    fun onClick()
}

// 使用对象表达式实现接口
val button = object : ClickListener {
    override fun onClick() {
        println("Button clicked!")
    }
}

button.onClick() // 输出: Button clicked!

2. 继承类并实现接口

对象表达式可以同时继承一个类和实现多个接口:

kotlin 复制代码
open class Animal(val name: String)
interface Flyable {
    fun fly()
}

val creature = object : Animal("Phoenix"), Flyable {
    override fun fly() {
        println("$name is flying!")
    }
}

creature.fly() // 输出: Phoenix is flying!

3. 纯匿名对象(不继承任何类)

直接创建一个匿名对象,定义属性和方法:

kotlin 复制代码
val data = object {
    val id = 1
    val name = "Temp"
    fun printInfo() = println("ID: $id, Name: $name")
}

data.printInfo() // 输出: ID: 1, Name: Temp

三、使用场景

1. 事件监听(如 UI 交互)

kotlin 复制代码
// 模拟 View 和点击事件
class View {
    var onClickListener: (() -> Unit)? = null
    fun click() = onClickListener?.invoke()
}

val view = View()
view.onClickListener = object : () -> Unit { // 等价于 SAM 转换的 Lambda
    override fun invoke() {
        println("View clicked")
    }
}

// 更简化的 Lambda 写法(优先推荐)
view.onClickListener = { println("View clicked") }

2. 临时实现回调接口

kotlin 复制代码
interface DownloadCallback {
    fun onSuccess(data: String)
    fun onError(error: Throwable)
}

fun downloadFile(callback: DownloadCallback) {
    // 模拟下载逻辑
    callback.onSuccess("File content")
}

// 使用对象表达式处理回调
downloadFile(object : DownloadCallback {
    override fun onSuccess(data: String) {
        println("下载成功: $data")
    }
    override fun onError(error: Throwable) {
        println("下载失败: ${error.message}")
    }
})

3. 替代工具类的临时扩展

kotlin 复制代码
fun String.toCustomFormat(): String {
    val formatter = object {
        fun addPrefix(s: String) = "[PREFIX]$s"
        fun addSuffix(s: String) = "$s[SUFFIX]"
    }
    return formatter.addSuffix(formatter.addPrefix(this))
}

println("Hello".toCustomFormat()) // 输出: [PREFIX]Hello[SUFFIX]

四、与 Java 匿名内部类的区别

特性 Kotlin 对象表达式 Java 匿名内部类
‌访问外部变量‌ 可修改非 final 变量 只能访问 final 或等效不可变变量
‌多继承‌ 支持同时继承类和实现多个接口 只能继承一个类或实现一个接口
‌SAM 转换‌ 支持 SAM 接口简化为 Lambda 需显式实现接口方法
‌语法简洁性‌ 无需 new 关键字,代码更简洁 需 new Interface() { ... }

五、注意事项

  1. ‌作用域限制 ‌

    匿名对象的成员仅在声明的作用域内可见:

    kotlin 复制代码
    fun createObject() = object {
        val x = 10
    }
    
    val obj = createObject()
    println(obj.x) // ❌ 编译错误:无法访问 'x'
  2. ‌性能开销 ‌

    频繁创建对象表达式可能带来内存压力(需权衡场景)。

  3. ‌与对象声明的区别‌

    • object 表达式:每次执行都创建新实例。
    • object 声明(单例对象):全局唯一实例。

六、综合案例

kotlin 复制代码
package com.derry.s5

interface RunnableKT {
    fun run()
}

open class KtBase88 {

    open fun add(info: String) = println("KtBase88 add:$info")

    open fun del(info: String) = println("KtBase88 del:$info")
}
// 1.add del println
// 2.匿名对象表达式方式
// 3.具名实现方式
// 4.对Java的接口 用对象表达式方式
fun main() {
    // 匿名对象 表达式方式
    val p : KtBase88 = object : KtBase88() {

        override fun add(info: String) {
            // super.add(info)
            println("我是匿名对象 add:$info")
        }

        override fun del(info: String) {
            // super.del(info)
            println("我是匿名对象 del:$info")
        }
    }
    p.add("李元霸")
    p.del("李连杰")


    // 具名实现方式
    val p2 = KtBase88Impl()
    p2.add("刘一")
    p2.del("刘二")

    // 对Java的接口 用   KT[对象表达式方式]  方式一
    val p3 = object : Runnable {
        override fun run() {
            println("Runnable run ...")
        }
    }
    p3.run()

    // 对Java的接口 用   Java最简洁的方式 方式二
    val p4 = Runnable {
        println("Runnable run2 ...")
    }
    p4.run()

    // 对KT的接口 用   KT[对象表达式方式]  方式一
    object : RunnableKT {
        override fun run() {
            println("RunnableKT 方式一 run ...")
        }
    }.run()

    // 对KT的接口 用   Java最简洁的方式 方式二
    /*RunnableKT {

    }*/
}

// 小结:Java接口,有两种方式 1(object : 对象表达式)  2简洁版,
//       KT接口,只有一种方式 1(object : 对象表达式)

// 具名实现  具体名字 == KtBase88Impl
class KtBase88Impl : KtBase88() {

    override fun add(info: String) {
        // super.add(info)
        println("我是具名对象 add:$info")
    }

    override fun del(info: String) {
        // super.del(info)
        println("我是具名对象 del:$info")
    }
}

七、总结

  • ‌优先使用对象表达式‌:临时实现接口、快速定义匿名类。
  • ‌避免滥用‌:在需要复用逻辑时,改用具名类或单例对象。
  • ‌灵活结合 Lambda‌:若接口是 SAM(单一抽象方法),优先使用 Lambda。
相关推荐
Android系统攻城狮3 小时前
Linux Gstreamer深度解析之gst_audio_encoder_set_frame_max调用流程与实战(六十一)
android·linux·运维·音视频·gstreamer音视频·音视频进阶·gstreamer音视频进阶
传奇开心果编程6 小时前
【Compose Multiplatform 跨端开发学与练】第7课 平台适配与互操作
android·windows·学习·ui·ios·kotlin·composer
mmsx6 小时前
Android 地图数据链路:从 GDAL、KML 到多引擎适配
android·kotlin
2601_968900776 小时前
大模型版本回归评估实战:从能力指标到额度口径的完整链路
android·数据挖掘·回归
ao-weilai9 小时前
MySQL数据库:基本查询
android·数据库·mysql
事圆则缓9 小时前
Kotlin 泛型方差实战:out、in、星投影与类型擦除
android·开发语言·kotlin
传奇开心果编程11 小时前
【Compose Multiplatform 跨端开发学与练】第5课 网络与数据层
android·网络·学习·ui·ios·kotlin·composer
ao-weilai12 小时前
MySQL数据库:内置函数
android·数据库·mysql
SWAGGY..12 小时前
【C++进阶】:(7)红黑树的原理与 C++ 实现:结构设计、插入调整及性质验证
android·java·开发语言·c++·算法
Android打工仔12 小时前
CoroutineScheduler 设计解析(上)—— 为什么 Dispatchers.IO 会创建更多线程?
android·kotlin·源码阅读