Kotlin中的对象/私有构造函数/伴侣对象之间的区别?

时间:2020-06-24 18:14:29

标签: android kotlin

请不要怪我。我只是从Java迁移到Kotlin。我正尝试在Singleton.getInstance().someMethod()的帮助下以常规java方式创建单例,并发现在Kotlin中可以使用几种不同的方法:

对象(单独的文件)object Singleton

伴侣对象companion object Factory {}

私有构造函数 class Singleton private constructor()

那么,请您帮我一下,解释一下我们在哪里可以输入什么类型?

3 个答案:

答案 0 :(得分:1)

object Singleton

  1. 线程安全单例
  2. 不是表达式
  3. 不能在赋值语句的右侧使用。
  4. 对象声明的初始化是线程安全的,并且在首次访问时完成
  5. 可以有超类型
  6. 对象声明不能是局部的(即直接嵌套在函数内部)
  7. 可以嵌套在其他对象声明或非内部类中

companion object Factory {}

  1. 可以仅通过使用类名(托管同伴)作为限定符来调用同伴对象的成员
  2. 伴侣对象的名称可以省略
  3. 伴随对象的成员看起来像其他语言中的静态成员,在运行时它们仍然是真实对象的实例成员
  4. 在加载(解析)相应的类时,将初始化一个伴随对象,该对象与Java静态初始化程序的语义匹配。

class Singleton private constructor()

我认为您不需要kotlin中的单例,因为kotlin已经提供了开箱即用的良好单例选项,但是正如其他SO用户所述,私有构造函数在kotlin中的作用与例如在java-防止实例化。再说一遍,如果您想在Kotlin中创建类似类的utils,请更好地考虑使用扩展功能。

PS ::应该很明显,所以我要提-上面的内容中有99%是从https://kotlinlang.org/docs/reference/object-declarations.html粘贴复制的-也许它有更好的机会在此处进行更多搜索:)

答案 1 :(得分:0)

如果您需要一个单例-一个只有一个实例的类-您可以按通常的方式声明该类,但可以使用object关键字而不是class。 如果需要将函数或属性绑定到类而不是实例(类似于Python中的@staticmethod),则可以在同伴对象中声明它。

当没有实例字段或方法(例如Math类)或调用方法以获取类的实例时,专用构造函数用于防止创建类的实例。

答案 2 :(得分:0)

Kotlin中的对象就像Java中的静态类。它们通常用于构造单例模式:

object Singleton

Java中的等效项是:

public static class Singleton{}

companion object用于必须应用Factory模式或静态工厂模式的情况(如您的伴随对象名称所述)。

假设我们使用Java:

public class Fragment(){
  private Fragment(){}

  public static Fragment newInstance(){
    return new Fragment();
  }
  
}

与Kotlin中的等效:

class Fragment private constructor(){
  companion object{
    fun newInstance() = Fragment()
  }
}

companion object也是一个对象,但是通过单词companion告诉JVM,该object所在的一个类可以访问其中的所有内容。

因此,如果您尝试从Java代码中调用它,它将是这样的:

Fragment.Companion.newInstance()

上面的示例实际上也适用于私有构造函数。 基本上,即使在Java中,当您不需要直接访问构造函数时,只需将构造函数标记为私有并使用静态工厂方法即可。

关于您的问题,使用上面提供的信息:

要实现

Singleton.getInstance().someMethod()

确切地说,您必须执行以下操作:

class Singleton private constructor(){
  companion object{
    fun getInstance() = Singleton()
    fun someMethod(){ /* Your implement here */}
  }
}

但是Kotlin风格并不太复杂。

只需:

object Singleton{
    fun someMethod(){ /* Your method here */}
}

然后就叫它:

Singleton.myMethod()

编辑:关于您的SharedPreferences问题,我不建议您使用object。您需要上下文和共享首选项模式的构造函数。因此,我将采用这样的方式(假设由于您在评论中提到了匕首,因此您一直在使用它):

class SharedPreferencesHelper @Inject constructor(val context: Context, val mode: Int) // not sure about the mode type but check the docs {

    private lateinit var sharedPreferences: SharedPreferences
    private lateinit var sharedPreferencesEditor: SharedPreferences.Editor

    init{
        sharedPreferences = context.getSharedPreferences("filename", mode)
        sharedPreferencesEditor = sharedPreferences.edit()
    }
    
}

然后,您只需在需要的任何构造函数中调用它即可。

或者:

class SharedPreferencesHelper private constructor(){
    private lateinit var sharedPreferences: SharedPreferences
    private lateinit var sharedPreferencesEditor: SharedPreferences.Editor
    
    companion object {
        fun startSharedPrefs(context: Context, fileName: String, mode: Int) = SharedPreferencesHelper().apply{
         sharedPreferences = context.getSharedPreferences(fileName, mode)
         sharedPreferencesEditor = sharedPreferences.edit()
        }
    }

}

然后在匕首模块中启动它:

@Module
object SharedPrefsModule{
    @Singleton
    @Provides
    fun provideSharedPreferences(application: Application) = 
     SharedPreferencesHelper.startSharedPrefs(application, "fileName", MODE_PRIVATE)

}

然后在需要的地方调用依赖项