简体   繁体   中英

Difference between object / private constructor / companion object in Kotlin?

Please don't blame me. Im just moving from Java to Kotlin. I'm trying to create a singleton as a regular java way with help of Singleton.getInstance().someMethod() and have found that in Kotlin there are several different things you can use:

Object (separate file) object Singleton

Companion object companion object Factory {}

Private constructor class Singleton private constructor()

So can you please help me and explain where we can you what type?

object Singleton

  1. Thread safe singleton
  2. Not an expression
  3. Cannot be used on the right hand side of an assignment statement.
  4. Object declaration's initialization is thread-safe and done at first access
  5. Can have supertypes
  6. Object declarations can't be local (ie be nested directly inside a function)
  7. Can be nested into other object declarations or non-inner classes

companion object Factory {}

  1. Members of the companion object can be called by using simply the class name (hosting companion) as the qualifier
  2. The name of the companion object can be omitted
  3. Members of companion objects look like static members in other languages, at runtime those are still instance members of real objects
  4. A companion object is initialized when the corresponding class is loaded (resolved), matching the semantics of a Java static initializer

class Singleton private constructor()

I don't think you need that for singleton in kotlin as kotlin already provides good singleton option out of the box, however as stated by other SO user here private constructor is serving the same purpose in kotlin as in, for example, java - to prevent instantiation. As a second thought, if you think of creating utils like classes in kotlin, please better consider using extension functions.

PS.: it should be pretty obvious so I'll mention it - 99% of the above is brutally copy-pasted from https://kotlinlang.org/docs/reference/object-declarations.html - may be it has better chances to be more searchable here:)

If you need a singleton - a class that only has got one instance - you can declare the class in the usual way, but use the object keyword instead of class. If you need a function or a property to be tied to a class rather than to instances of it (similar to @staticmethod in Python), you can declare it inside a companion object.

Private constructors are used to prevent creating instances of a class when there are no instance fields or methods, such as the Math class, or when a method is called to obtain an instance of a class.

Objects in Kotlin are like static classes in Java. They are usually used to construct the singleton pattern:

object Singleton

The equivalent in Java would be:

public static class Singleton{}

The companion object is used in cases (as your companion object name states) where you have to apply the Factory pattern or the static factory pattern.

Let's suppose we have this in Java:

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

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

The equivalent of that in Kotlin would be:

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

The companion object is also an object but through the word companion is just telling JVM that the one class in which this object is, has access to everything inside it.

Therefore if you try to call it from Java code, it would be something like this:

Fragment.Companion.newInstance()

The above example actually also fits for private constructor. Basically, even in Java, when you don't need to access the constructor directly, just mark the constructor as private and use a static factory method.

Regarding your question, with the information provided above:

To achieve

Singleton.getInstance().someMethod()

with exactly this call, you have to do this:

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

However that's not too sophisticated in Kotlin style.

Just do:

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

Then just call it:

Singleton.myMethod()

EDIT: Regarding your question of SharedPreferences I don't suggest to use an object for that. You need the constructor for the context and perhaps shared preferences mode. Therefore I would go with something like this (assuming that you are using dagger since you mentioned it in comment):

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()
    }
    
}

Then you just call it in any constructor you need it.

Or:

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()
        }
    }

}

Then start it in a dagger module:

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

}

Then call the dependency wherever you need it

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM