【Kotlin】匿名类、单例类和伴生对象 object、companion

在这里插入图片描述

匿名类

定义

在 Kotlin 中,“匿名类”指的是没有显式名称的类定义,通常是通过对象表达式(object expression) 来创建的一个对象实例。

💡 简单理解:匿名类 = 定义一个临时类 + 立即实例化一个对象。

匿名类在 Kotlin 中通过对象表达式(Object Expression) 来实现

val obj = object {
    val x = 10
    fun show() = println("x = $x")
    override fun toString(): String = "我是一个匿名类"   // 匿名类默认继承于Any,可以直接重写其toString
}

obj.show()   // 输出:x = 10
println(obj) // 

这里的 object { ... } 创建了一个没有名字的类,并立即实例化。

可以在匿名类中定义:

  • 属性(valvar
  • 方法
  • 初始化块(init
val obj = object {
    val name = "Kotlin"
    init {
        println("Initializing $name")
    }
    fun greet() = println("Hello, $name!")
}
obj.greet()
// Initializing Kotlin
// Hello, Kotlin!

匿名类不仅可以直接定义,也可以继承某个类,或实现某个接口:

interface Person {
    fun chat()
}
open class Human(val name: String)

fun main() {
    val obj: Human = object : Human("小明"), Person {   // 继承类时,同样需要调用其构造函数
        override fun toString() = "我叫$name"   // 使用父类的属性
    }
    println(obj)
}

继承抽象类

abstract class A {
    abstract fun greet()
}

interface B {
    fun sayBye()
}

val obj = object : A(), B {
    override fun greet() = println("Hi!")
    override fun sayBye() = println("Bye!")
}

obj.greet()
obj.sayBye()

像这样,之前无法实例化的接口或抽象类,可以通过匿名类的形式得到一个实例化对象。

为什么需要匿名类

匿名类存在的主要目的是:

  • 临时创建对象(例如:事件监听器、回调函数、一次性使用的对象)
  • 扩展已有类或实现接口而不需要额外定义类名
  • 实现抽象类或接口的匿名实现

匿名类不能声明构造函数(因为没有类名可供定义构造函数)

但是可以调用父类构造函数

open class Base(val msg: String)

val obj = object : Base("Hello") {
    fun show() = println(msg)
}
obj.show()   // Hello

匿名类与函数式接口(SAM 接口)

函数式接口(Functional Interface)是指只包含一个抽象方法的接口,但可以有多个非抽象成员(default、static 方法)、多个常量属性

在 Kotlin 中用 fun interface 关键字声明:

fun interface ClickListener {
    fun onClick()
}

匿名类实现函数式接口

// 传统方式
val listener = object : ClickListener {
    override fun onClick() {
        println("Clicked!")
    }
}

// Lambda 简化
val listener = ClickListener { println("Clicked!") }

listener.onClick()

这种写法称为 SAM 转换(Single Abstract Method conversion),Kotlin 会自动将 Lambda 转换为该接口的匿名类实例。

函数式接口对象作为函数参数

fun interface Printer {
    fun print()
}

fun test(printer: Printer) {
    printer.print()
}

// 调用
test {   // Lambda
    println("Hello World")
}

匿名类的作用域与类型

如果匿名对象被赋值给一个具有显式类型的变量,那么编译器只知道它的父类型

interface A { fun show() }

val a: A = object : A {
    val x = 10
    override fun show() = println("x=$x")
}

a.show()        // ✅ 可调用
// println(a.x) // ❌ 无法访问,因为类型是 A,而不是匿名类型

如果没有显式类型,Kotlin 会保留匿名类的真实类型信息:

val a = object : A {      // 这里a类型:<anonymous object : A>
    val x = 10
    override fun show() = println("x=$x")
}

println(a.x)    // ✅ 可调用

函数返回匿名类对象同样

open class Human(val name: String)

fun test() = object: Human("小明") {
  	val age: Int = 10
    override fun toString() = "我叫$name"
}

val human1 = test()         // 这里human1类型:<anonymous object : Human>
println(human1.name)
println(human1.age)

val human2: Human = test()
println(human1.name)
println(human1.age)        // 编译错误

单例类

单例(Singleton)指一个类在整个程序运行过程中只有一个实例(对象),并且提供一个全局访问点。

在许多情况下,我们只希望某个类存在唯一的实例,例如:

  • 管理全局配置(如 ConfigManager
  • 管理数据库连接(DatabaseHelper
  • 管理日志输出(Logger
  • 管理网络请求队列(NetworkManager

在 Java 中,通常需要写很多代码来实现单例,而在 Kotlin 中,只需要用一个关键字:object

object Singleton {
    private var count = 0

    fun showCount() {
        println("Count = $count")
    }
    
    fun addCount() {
        count += 1
    }
    
    override fun toString() = "count——$count"
}

fun main() {
    Singleton.count++       // 编译错误,private
    Singleton.addCount()    // 单例定义的函数直接使用类名即可调用
    Singleton.showCount()   // Count = 1

    val a = Singleton  // 通过类名直接得到此单例类的对象
    val b = Singleton
    val c = Singleton()  // 编译错误,不可以通过构造函数的形式创建对象
    println(a === b)  // true,说明 a 和 b 是同一个对象
    
    a.showCount()     // 不推荐
    
    println(a)        // count——1
}

伴生对象

伴生对象(companion object)是类内部的一个对象,它允许我们在类内部声明一些类似 Java 静态成员的属性和方法。

在 Kotlin 中没有 static 关键字,伴生对象就是 Kotlin 对应的静态成员实现方式

class MyClass {
    companion object {
        val CONSTANT = 100
        fun greet() = println("Hello from companion object")
    }
}

fun main() {
    println(MyClass.CONSTANT)   // 100
    MyClass.greet()             // Hello from companion object
}

也可以给伴生对象起名字

class MyClass {
    companion object Factory {
        fun create() = MyClass()
    }
}

val obj = MyClass.create()  // 使用名字也可以 MyClass.Factory.create()

可以在伴生对象中定义静态工厂方法来创建类实例。

class User private constructor(val name: String) {
    companion object {
        fun create(name: String): User = User(name)
    }
}

fun main() {
    val user = User.create("Alice")
    println(user.name)  // Alice
}

伴生对象可以实现接口

interface Factory<T> {
    fun create(): T
}

class Product {
    companion object : Factory<Product> {
        override fun create(): Product = Product()
    }
}

伴生对象是单例

每个类只会有一个伴生对象实例,伴生对象实例在第一次访问类时被初始化(lazy)。

class Example {
    companion object {
        init { println("Companion object initialized") }
    }
}

fun main() {
    println("Before accessing")
    Example // 还没访问 companion
    println("After accessing")
}
/*
Before accessing
Companion object initialized
After accessing
*/

通过 @JvmStatic 注解生成 Java 风格静态方法,以便 Java 代码调用

class MyClass {
    companion object {
        @JvmStatic
        fun staticMethod() = println("Java static style")
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shayudiandian

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值