Kotlin 中的单例类
Posted
技术标签:
【中文标题】Kotlin 中的单例类【英文标题】:Singleton class in Kotlin 【发布时间】:2019-01-20 22:21:27 【问题描述】:我想知道如何在 Kotlin 中创建一个单例类,以便我的 Util 类在每次应用执行时只实例化一次。但是,当我将我的 Java 类转换为 kotlin 时,生成了以下代码。
这是正确的吗?
companion object
private var utilProject: UtilProject? = null
val instance: UtilProject
get()
if (utilProject == null) utilProject = UtilProject()
return utilProject!!
我可以找到一个相关的 question,但它是带参数的,如果没有参数,我无法将其转换。
【问题讨论】:
【参考方案1】:在 Kotlin 中有一个特殊的关键字 object
用于单例。你可以输入一些简单的东西来获得工作单例类:
object MySingleton
或者当你想要一些成员函数时:
object MySingleton
fun someFunction(...) ...
然后使用它:
MySingleton.someFunction(...)
有参考:https://kotlinlang.org/docs/reference/object-declarations.html#object-declarations
编辑:
在您的情况下,您只需将 class UtilProject
的定义替换为:
object UtilProject
// here you put all member functions, values and variables
// that you need in your singleton Util class, for example:
val maxValue: Int = 100
fun compareInts(a: Int, b: Int): Int ...
然后你可以简单地在其他地方使用你的单例:
UtilProject.compareInts(1, 2)
//or
var value = UtilProject.maxValue
【讨论】:
我没有得到这个。这对我的 Util 类有何用处? 反对创建companion object
,只需将class UtilProject
的声明更改为object UtilProject
喜欢@Naetmul 的回答?
实际上,使用object
而不是class
是你的单身。 mentioned reference above 值得一读;-)
使用object
代替class
是最优化的解决方案。如果您想从 JAVA 类调用对象内部的方法,可以使用 Singleton.INSTANCE.methodeName()..
【参考方案2】:
只是
companion object
val instance = UtilProject()
将完成这项工作,因为 伴随对象 本身是语言级别的单例。
(instance
将在 first 调用伴生对象时创建。)
-- 更新了--
如果需要控制单例对象的初始化时间,可以为每个类创建一个对象。
class UtilProject
....
companion object
val instance = UtilProject()
class AnotherClass
...
companion object
val instance = AnotherClass()
const val abc = "ABC"
fun main(args: Array<String>)
val a = UtilProject.instance // UtilProject.instance will be initialized here.
val b = AnotherClass.abc // AnotherClass.instance will be initialized here because AnotherClass's companion object is instantiated.
val c = AnotherClass.instance
这里,AnotherClass.instance
在实际调用 AnotherClass.instance
之前被初始化。它在调用AnotherClass
的伴随对象时被初始化。
为了防止它在需要时被初始化,你可以这样使用:
class UtilProject
....
companion object
fun f() = ...
class AnotherClass
...
companion object
const val abc = "ABC"
object UtilProjectSingleton
val instance = UtilProject()
object AnotherClassSingleton
val instance = AnotherClass()
fun main(args: Array<String>)
UtilProject.f()
println(AnotherClass.abc)
val a = UtilProjectSingleton.instance // UtilProjectSingleton.instance will be initialized here.
val b = AnotherClassSingleton.instance // AnotherClassSingleton.instance will be initialized here.
val c = UtilProjectSingleton.instance // c is a.
如果你不关心每个单例的初始化时间,你也可以这样使用:
class UtilProject
....
companion object
fun f() = ...
class AnotherClass
...
companion object
const val abc = "ABC"
object Singletons
val utilProject = UtilProject()
val anotherClass = AnotherClass()
fun main(args: Array<String>)
val a = Singletons.utilProject
val b = Singletons.anotherClass
总之,object
或 companion object
是 Kotlin 中的一个单例对象。
您可以在 object 或 objects 中分配变量,然后像使用单例一样使用这些变量。
object
或 companion object
在第一次使用时被实例化。
object
中的 val
s 和 var
s 在首次实例化 object
时(即首次使用 object
时)进行初始化。
【讨论】:
所有对象都在companion
singleton里面吗?
@Khemraj 没有。companion object
是一个单例对象。如果您需要许多不同的单例,您可以为每个单例创建许多对象。我会更新答案。
很好的解释!,我仍然需要一年的时间来理解这种新的语言语法。
这不是最好的答案,因为创建 util 类的最好方法是使用 object 关键字代替 class 关键字 - 那么它是语言级别的单例,我们不必费心创建这样一个类的实例。我们只是像 Java 的静态方法一样使用函数。请看我的回答
单例的初始化怎么样?如果ojbect
不能有构造函数,我们应该使用初始化块(init ...
)吗?【参考方案3】:
超级简单的懒惰例子:
companion object
val instance: UtilProject by lazy UtilProject()
【讨论】:
不需要!只需使用对象关键字 是的。实际上,object 关键字也是如此【参考方案4】:只需要词对象。
object UtilProject
var bar: Int = 0
fun foo()
而你直接访问只有一个实例的对象
fun main(args: Array<String>)
UtilProject.bar = 1
println(UtilProject.bar)
【讨论】:
【参考方案5】:在 Kotlin 中,您应该摆脱实用程序单例类的整个概念。惯用的方法是简单地将所有声明移到顶层。
Java:
public final class Util
public static final Util UTIL = new Util();
private int prefixLength = 4;
private Util()
public void setPrefixLength(int newLen)
prefixLength = newLen;
public String extractVin(String input)
return input.substring(prefixLength);
用法:
String vin = UTIL.extractVin("aoeuVN14134230430")
在 Kotlin 中,只需使用以下内容创建一个名为 util.kt
的单独文件:
var prefixLength = 4
fun String.extractVin() = this.substring(prefixLength)
用法:
val vin = "aoeuVN14134230430".extractVin()
但是……你正在污染***命名空间!
如果您的 Java 直觉在这里触发了危险信号,请记住 包 是命名空间结构,与 Java 不同,Kotlin 不会将命名空间和封装的问题混为一谈。没有“包私有”访问级别,因此您无需决定某些内容必须保留在同一个包中,以便将其设为包私有。
因此,在 Java 中创建退化类作为解决方法,在 Kotlin 中,您只需在其自己的包中创建一个文件。
【讨论】:
【参考方案6】: class TestMySingleton private constructor()
companion object
var single = TestMySingleton()
fun getInstance(): TestMySingleton
if (single == null)
single = TestMySingleton()
return single
【讨论】:
【参考方案7】:一个Singleton
示例,经过改造以支持 api 调用。
object RetrofitClient
private var instance: Api? = null
private val BASE_URL = "https://jsonplaceholder.typicode.com/"
fun getInstance(): Api?
if (instance == null)
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
instance = retrofit.create(Api::class.java)
return instance
【讨论】:
您实现getInstance()
方法的方式不正确。 getInstance()
内部的逻辑表示它将始终返回 non-null
值,但您的返回类型 (Api?
) 表示它可以返回 null
值。如果您尝试将返回类型重构为 Api
,您将收到 Smart cast to a mutable property is not possible
警告。【参考方案8】:
带参数的变体
open class SingletonHolder<out T: Any, in A>(creator: (A) -> T)
private var creator: ((A) -> T)? = creator
@Volatile private var instance: T? = null
fun getInstance(arg: A): T
val checkInstance = instance
if (checkInstance != null)
return checkInstance
return synchronized(this)
val checkInstanceAgain = instance
if (checkInstanceAgain != null)
checkInstanceAgain
else
val created = creator!!(arg)
instance = created
creator = null
created
【讨论】:
一个很好的答案,但它只是整个事情的一部分。这段代码是从这里复制过来的:blog.mindorks.com/how-to-create-a-singleton-class-in-kotlin 看一看就知道全貌了,为什么要这样处理。【参考方案9】:class MyClass
init
println("init is called")
companion object
private var obj: MyClass? = null
fun getInstance(): MyClass
if (obj == null)
obj = MyClass()
return obj as MyClass
fun printHello()
println("Hello World")
您可以通过MyClass.getInstance()
之类的 java 来创建它的实例
【讨论】:
【参考方案10】:这会有所帮助。我正在使用Dialog
类,但您可以使用示例来了解如何实现。
class MyClass(context: Context) : Dialog(context)
companion object
lateinit var INSTANCE: MyClass
@JvmStatic
fun getInstance(context: Context): MyClass
if (!::INSTANCE.isInitialized)
INSTANCE = MyClass(context)
return INSTANCE
【讨论】:
【参考方案11】:这里的所有答案大部分都是正确的,除非是线程处理。我的用例是这样的
使用不同的线程同时调用这两种方法:
private fun getProductListSync()
launch(Dispatchers.Main)
products = withContext(Dispatchers.IO) getProducts()
private suspend fun getProducts(): List<Product>?
val client = APIUtils.getClient() // this method is used for getting Retrofit Client
val productListCall = client.create(APIBuilder::class.java).getProductList()
return if (productListCall.isSuccessful)
...
else
...
private fun getRestaurantDetailsSync()
launch(Dispatchers.Main)
storeInfo = withContext(Dispatchers.IO) getStoreInfo()
private suspend fun getStoreInfo(): StoreInfo?
val client = APIUtils.getClient()
val storeInfoCall = client.create(APIBuilder::class.java).getStoreInfo()
return if (storeInfoCall.isSuccessful)
...
else
...
调用代码:
getRestaurantDetailsSync()
getProductListSync()
用于多线程处理的单例模式的 APIUtils 的正确代码
APIUtils.kt
object APIUtils
@Volatile
private var retrofit: Retrofit? = null
/**
* You can create multiple methods for different BaseURL
*
* @return [Retrofit] object
*/
@Synchronized
fun getClient(): Retrofit
if (retrofit == null)
retrofit = Builder()
.baseUrl(Constants.API.BASE_URL)
.build()
return retrofit!!
fun destroy()
retrofit = null
注意:这里,如果我们不在字段上使用@Volatile,在函数上不使用@Synchronized,当从不同线程调用时,它会创建多个改造字段副本。
您还可以重新分配改造客户端以应用额外的静态标头,因为我们使用 "var" 关键字而不是 "val" 或 "lateinit var"强>
【讨论】:
以上是关于Kotlin 中的单例类的主要内容,如果未能解决你的问题,请参考以下文章