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。
相关推荐
mmoyula2 小时前
【RK3568 驱动开发:实现一个最基础的网络设备】
android·linux·驱动开发
sam.li2 小时前
WebView安全实现(一)
android·安全·webview
移动开发者1号3 小时前
Kotlin协程超时控制:深入理解withTimeout与withTimeoutOrNull
android·kotlin
程序员JerrySUN3 小时前
RK3588 Android SDK 实战全解析 —— 架构、原理与开发关键点
android·架构
移动开发者1号4 小时前
Java Phaser:分阶段任务控制的终极武器
android·kotlin
哲科软件12 小时前
跨平台开发的抉择:Flutter vs 原生安卓(Kotlin)的优劣对比与选型建议
android·flutter·kotlin
jyan_敬言19 小时前
【C++】string类(二)相关接口介绍及其使用
android·开发语言·c++·青少年编程·visual studio
程序员老刘19 小时前
Android 16开发者全解读
android·flutter·客户端
福柯柯20 小时前
Android ContentProvider的使用
android·contenprovider
不想迷路的小男孩20 小时前
Android Studio 中Palette跟Component Tree面板消失怎么恢复正常
android·ide·android studio