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。
相关推荐
_小马快跑_2 小时前
Android | 利用ItemDecoration绘制RecyclerView分割线
android
_小马快跑_2 小时前
别再手写 if/else 判断了!赶紧来掌握 Kotlin 的 coerce 三兄弟吧
android
_小马快跑_2 小时前
Android Xfermode应用:实现圆角矩形、圆形等图片裁切
android
怀旧,3 小时前
【数据结构】4.单链表实现通讯录
android·服务器·数据结构
yechaoa3 小时前
Widget开发实践指南
android·前端
顾林海5 小时前
Flutter 图标和按钮组件
android·开发语言·前端·flutter·面试
匹马夕阳6 小时前
(二十二)安卓开发中的数据存储之SQLite简单使用
android·数据库·sqlite
_一条咸鱼_6 小时前
大厂Android面试秘籍:上下文管理模块
android·面试·android jetpack
mingzhi616 小时前
绿盟二面面试题
android·web安全·渗透测试
SY.ZHOU8 小时前
Flutter 与原生通信
android·flutter·ios