在 Kotlin 中,对象表达式(Object Expression) 是一种快速创建匿名类实例的语法,类似于 Java 的匿名内部类,但更简洁灵活。它适用于需要临时实现接口或继承类的场景。
一、核心特性
- 匿名性
对象表达式没有显式类名,直接通过object
关键字声明。 - 临时性
通常在局部作用域内使用,无法在其他地方复用。 - 捕获外部变量
可以访问并修改其作用域内的变量(与 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() { ... } |
五、注意事项
-
作用域限制
匿名对象的成员仅在声明的作用域内可见:
kotlinfun createObject() = object { val x = 10 } val obj = createObject() println(obj.x) // ❌ 编译错误:无法访问 'x'
-
性能开销
频繁创建对象表达式可能带来内存压力(需权衡场景)。
-
与对象声明的区别
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。