在Kotlin中,定义一个单例类非常简单且直观,得益于语言内置的object关键字。这个关键字不仅简化了单例的实现,还避免了传统Java单例模式中的样板代码。
1,在kotlin中通过object定义一个单例类。
在一个HelloSingleton.kt文件中,定义两个单例类:
object HelloSingleton {
private var sProp:String = "hello singleton"
fun sayHello() {
println(sProp)
}
}
object MySingleton {
// 类的成员变量
var someProperty: String = "Hello, World!"
// 类的成员函数
fun doSomething() {
println("Doing something in MySingleton!")
}
}
fun main() {
println("=======================")
HelloSingleton.sayHello()
MySingleton.doSomething()
MySingleton.someProperty = "new Value"
println(MySingleton.someProperty)
}
HelloSingleton和MySingleton都是通过object关键字定义的,它自动成为了一个单例,这意味着在Kotlin程序的整个生命周期中,HelloSingleton和MySingleton分别只会有一个实例。
在main函数中,我们直接通过MySingleton的类名来访问其属性和方法,无需进行实例化。这体现了Kotlin单例实现的简洁性。
上面定义的单例类是饿汉式单例。
2,结合类的私有构造函数和伴生对象来实现单例模式。
不带参数的方式
class Singleton private constructor() {
// 类的成员
fun doSomething() {
println("Doing something in Singleton!")
}
// 伴生对象,提供对单例实例的访问
companion object {
// 使用by lazy实现延迟初始化,确保线程安全
val instance: Singleton by lazy { Singleton() }
}
}
在main()函数中,
Singleton.instance.doSomething()
下面看下带参数的方式:
class SingletonA private constructor(id: String) {
val instanceId: String = id
companion object {
private var mInstance: SingletonA? = null
@Synchronized
fun getInstance(id: String): SingletonA {
return mInstance ?: SingletonA(id).also { mInstance = it }
}
}
}
在main()函数中,
println(SingletonA.getInstance("hello").instanceId)
这样的方式可以实现懒汉单例模式,但推荐采用第一种object的方式定义单例。
3,对于object,除了定义单例类对象外,还可以用于修饰对象表达式。
open class SingleTest {
open fun play() = "loading..."
}
之后在main函数中
fun main() {
//使用对象表达式创建了一个SingleTest的匿名子类实例,并重写了play()方法
val p = object : SingleTest(){
override fun play() = "other loading..."
}
println(p.play()) //输出other loading...
}
在上面的代码中,定义了一个open类型的类。
在Kotlin中,定义一个类时,你可以通过指定关键字来控制这个类的可继承性。主要有两种类型的类:final(默认,不可被继承)和open(可被继承)。
Kotlin中的抽象类(使用abstract关键字)默认也是开放的,因为它们的主要目的之一就是作为其他类的基类。
abstract class MyAbstractClass {
// 抽象成员
abstract fun myAbstractFunction(): String
// 非抽象成员(可以有默认实现)
fun myNonAbstractFunction() {
// 函数实现
}
}
即使在开放类中,也可以通过省略open关键字来定义封闭的函数和属性,这意味着它们不能在子类中被重写或修改。
open class MyClassWithFinalMember {
final val myFinalProperty = "I cannot be overridden"
fun myFinalFunction() {
// 这个函数不能被重写
}
}
上面的代码中,属性和方法定义省略了open,那么myFinalProperty是一个封闭的属性,而myFinalFunction是一个封闭的函数。尝试在子类中重写myFinalFunction或修改myFinalProperty的值将导致编译错误。
在Kotlin中,你可以通过open关键字来定义一个可以被继承的类,而省略该关键字则会使类默认不可被继承。对于函数和属性,你也可以通过省略open关键字来使它们封闭(不可被重写或修改)。
在 Kotlin 中,使用 object 关键字可以方便地创建一个匿名内部类的实例。
private val pageChangeListener = object : ViewPager.OnPageChangeListener {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
Log.v(TAG, "pageChangeListener,onPageScrolled...position:$position")
}
override fun onPageSelected(position: Int) {
Log.v(TAG, "pageChangeListener,onPageSelected...position:$position")
}
override fun onPageScrollStateChanged(state: Int) {
Log.v(TAG, "pageChangeListener,onPageScrollStateChanged...state:$state")
}
}
上面的代码将 ViewPager.OnPageChangeListener 的匿名内部类转换为 Kotlin 的对象表达式。
4,访问单例类的函数
从上面已经知道在 Kotlin 里,使用 object 关键字来定义单例类。那怎么访问单例类中的函数呢?
在kotlin类文件中,要访问单例类的函数,无需创建该类的实例,直接通过单例类的名称来调用函数即可。即 ”单例类类名“."函数"。
在java类文件中,要访问单例类的函数,需要使用 INSTANCE 字段。即”单例类类名“.INSTANCE."函数"。
在 Java 里,单例模式通常需要手动编写代码来保证类只有一个实例,并且会提供一个静态的 INSTANCE 字段来获取该实例。
在 Kotlin 中,使用 object 关键字定义单例类时,Kotlin 编译器会自动处理单例的创建和访问,不需要显式地使用 INSTANCE 字段。
kotlin单例类与 Java 代码交互:
如果项目是 Kotlin 和 Java 混合编程,在 Java 代码中访问 Kotlin 的单例类时,就需要使用 INSTANCE 字段。 因为 Kotlin 的 object 单例类在编译成 Java 字节码后,会生成一个静态的 INSTANCE 字段来表示单例实例。