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?
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
object Singleton
Thread safe singleton
Not an expression
Cannot be used on the right hand side of an assignment statement.
Object declaration's initialization is thread-safe and done at first access
Can have supertypes
Object declarations can't be local (i.e. be nested directly inside a function)
Can be nested into other object declarations or non-inner classes
companion object Factory {}
Members of the companion object can be called by using simply the class name (hosting companion) as the qualifier
The name of the companion object can be omitted
Members of companion objects look like static members in other languages, at runtime those are still instance members of real objects
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.
Related
I am learning dependency injection with Hilt and Dagger and I want to know... When creating classes with the Singleton pattern, should I use a real Singleton or the Hilt annotation? I searched on the internet but could not find a conclusive solution that shows the difference, also when I click to open the generated file from Hilt it does not looks like a Singleton, is it "threadissuesproof"?
"Real Singleton", like this:
class SongController private constructor() {
companion object {
#Volatile
private var INSTANCE: SongController? = null
fun getInstance(): SongController {
synchronized(this) {
var instance = INSTANCE
if (instance == null) {
instance = SongController()
INSTANCE = instance
}
return instance
}
}
}
}
Or the #Singleton annotation from Hilt?
#Singleton
class SongController #Inject constructor() {}
Is there any benefits of using the annotation or the best and safe option is still the getInstace() -> synchronized() block?
Which one should I use in this case?
And the most important question that I did not understand, will the annotation behave the same way?
In most cases you should prefer the DI framework version of a singleton. It is not exactly the same, but effectively, to your app that uses DI to set everything up, there is only one instance. This is preferred to a traditional singleton, because it will allow you to swap in alternate versions for testing.
Regarding the comments: object most definitely does not cover the case of a singleton that relies on constructor properties.
By the way, your traditional singleton code could be improved to use double-checked locking to avoid having to synchronize on every access forever. Actually, the way your code is now, there's no need for Volatile, but you do need it for double-checked locking. Here's an example:
class SongController private constructor(someParameter: SomeType) {
companion object {
#Volatile
private var INSTANCE: SongController? = null
fun getInstance(someParameter: SomeType): SongController = INSTANCE ?: synchronized(this) {
INSTANCE ?: SongController(someParameter).also { INSTANCE = it }
}
}
}
After spending a ludicrous amount of time trying to figure out why my dagger injections weren't working; I realised that the "object" type in Kotlin was the problem.
The following did not work, the injected "property" was null.
object SomeSingleton {
#Inject
lateinit var property: Property
init {
DaggerGraphController.inject(this)
}
}
However, the following DID work just fine:
class NotSingleton {
#Inject
lateinit var property: Property
init {
DaggerGraphController.inject(this)
}
}
I tried google, I tried the documentation but I could not pin point the reason behind this. Also note that I havent tried this with JAVA, JAVA doesnt have the concept of singletons built in anyway.
Why is this the case? Why is a kotlin singleton unable to inject members but a regular non-singleton class can?
If you look into kotlin bytecode you'll find that the code you've written is translated into following:
public final class SomeSingleton {
public static LProperty; property // <- Notice static field here
public final getProperty()LProperty
...
public final setProperty(LProperty)V
...
}
As you can see the actual field is static which makes it uneligible for instance injection. You may try to move #Inject annotation onto setter method by doing so:
object SomeSingleton {
#set:Inject
lateinit var property: Property
...
}
A workaround for this can be to extend a BaseClass that consists of the fields to be injected.
object SomeSingleton : BaseClass {
...
...
}
open class BaseClass{
#Inject
lateinit var property: Property
init{
YourDaggerComponent.inject(this)
}
}
This does work, although this would leak this, which comes up as an android studio warning, to get rid of that make the Base class abstract and instead inject the fields in your original object class
object SomeSingleton : BaseClass {
...
...
// Add the init block here instead of the base class
init{
YourDaggerComponent.inject(this)
}
}
abstract class BaseClass{
#Inject
lateinit var property: Property
//Remove the init block from here
}
And you Dagger AppComponent interface can be like, any of those function def should work
interface Component{
fun inject(someSingleton : SomeSingleton)
//OR
fun inject(baseClass: BaseClass)
}
I hope this helps....
I tried to use dagger.Lazy<YourClass> and it works
#set:Inject
lateinit var authClient: dagger.Lazy<PlatformAuthClient>
You may still need the #Singleton decorator on top of your object definition. That decorator doesn't make your class 'singleton', it's just used by Dagger to get all the dependencies in the same spot.
This was previously allowed by a bug in Dagger. As others described, it is because the properties in a kotlin object are backed by static fields.
See https://github.com/google/dagger/issues/1665 for the fix. It was fixed in 2.27.
I have a Exception Handling class
class IOException : BaseException {
#EntryPoint
#InstallIn(SingletonComponent::class)
interface AnalyticsServiceProviderEntryPoint {
fun analyticsService(): AnalyticsService
}
private val hiltEntryPoint = EntryPointAccessors.fromApplication(**Need Context**, AnalyticsServiceProviderEntryPoint::class.java)
val analyticsService = hiltEntryPoint.analyticsService()
}
If I see this offical link, it says
In this example, you must use the ApplicationContext to retrieve the
entry point because the entry point is installed in SingletonComponent
What If I don't have the context in the class and in the function body which I will use and I don't want to use from Constructor Injection as well?
I only want to use the field injection. How can I access it, since I don't have the context.
In your application add a companion object with lateinit var applicationContext and initialize the variable when the application is initialized like:
companion object {
lateinit var applicationContext: Context
private set
then you have an static variable with the application context and you can you something like:
private val hiltEntryPoint = EntryPointAccessors.fromApplication(Application.applicationContext, AnalyticsServiceProviderEntryPoint::class.java)
I can't think of anything else to do what you want to do
Did you try to get applicationContext from dagger-hilt ApplicationContextModule? Its already provided by this module in your app and you can get that probably. You need to use the qualifer also
#Provides
#ApplicationContext
Context provideContext() {
return applicationContext;
}
After spending a ludicrous amount of time trying to figure out why my dagger injections weren't working; I realised that the "object" type in Kotlin was the problem.
The following did not work, the injected "property" was null.
object SomeSingleton {
#Inject
lateinit var property: Property
init {
DaggerGraphController.inject(this)
}
}
However, the following DID work just fine:
class NotSingleton {
#Inject
lateinit var property: Property
init {
DaggerGraphController.inject(this)
}
}
I tried google, I tried the documentation but I could not pin point the reason behind this. Also note that I havent tried this with JAVA, JAVA doesnt have the concept of singletons built in anyway.
Why is this the case? Why is a kotlin singleton unable to inject members but a regular non-singleton class can?
If you look into kotlin bytecode you'll find that the code you've written is translated into following:
public final class SomeSingleton {
public static LProperty; property // <- Notice static field here
public final getProperty()LProperty
...
public final setProperty(LProperty)V
...
}
As you can see the actual field is static which makes it uneligible for instance injection. You may try to move #Inject annotation onto setter method by doing so:
object SomeSingleton {
#set:Inject
lateinit var property: Property
...
}
A workaround for this can be to extend a BaseClass that consists of the fields to be injected.
object SomeSingleton : BaseClass {
...
...
}
open class BaseClass{
#Inject
lateinit var property: Property
init{
YourDaggerComponent.inject(this)
}
}
This does work, although this would leak this, which comes up as an android studio warning, to get rid of that make the Base class abstract and instead inject the fields in your original object class
object SomeSingleton : BaseClass {
...
...
// Add the init block here instead of the base class
init{
YourDaggerComponent.inject(this)
}
}
abstract class BaseClass{
#Inject
lateinit var property: Property
//Remove the init block from here
}
And you Dagger AppComponent interface can be like, any of those function def should work
interface Component{
fun inject(someSingleton : SomeSingleton)
//OR
fun inject(baseClass: BaseClass)
}
I hope this helps....
I tried to use dagger.Lazy<YourClass> and it works
#set:Inject
lateinit var authClient: dagger.Lazy<PlatformAuthClient>
You may still need the #Singleton decorator on top of your object definition. That decorator doesn't make your class 'singleton', it's just used by Dagger to get all the dependencies in the same spot.
This was previously allowed by a bug in Dagger. As others described, it is because the properties in a kotlin object are backed by static fields.
See https://github.com/google/dagger/issues/1665 for the fix. It was fixed in 2.27.
This question already has answers here:
Singleton with parameter in Kotlin
(14 answers)
Closed 2 years ago.
The Kotlin reference says that I can create a singleton using the object keyword like so:
object DataProviderManager {
fun registerDataProvider(provider: DataProvider) {
//
}
}
However, I would like to pass an argument to that object. For example an ApplicationContext in an Android project.
Is there a way to do this?
Since objects do not have constructors what I have done the following to inject the values on an initial setup. You can call the function whatever you want and it can be called at any time to modify the value (or reconstruct the singleton based on your needs).
object Singleton {
private var myData: String = ""
fun init(data: String) {
myData = data
}
fun singletonDemo() {
System.out.println("Singleton Data: ${myData}")
}
}
Kotlin has a feature called Operator overloading, letting you pass arguments directly to an object.
object DataProviderManager {
fun registerDataProvider(provider: String) {
//
}
operator fun invoke(context: ApplicationContext): DataProviderManager {
//...
return this
}
}
//...
val myManager: DataProviderManager = DataProviderManager(someContext)
With most of the existing answers it's possible to access the class members without having initialized the singleton first. Here's a thread-safe sample that ensures that a single instance is created before accessing any of its members.
class MySingleton private constructor(private val param: String) {
companion object {
#Volatile
private var INSTANCE: MySingleton? = null
#Synchronized
fun getInstance(param: String): MySingleton = INSTANCE ?: MySingleton(param).also { INSTANCE = it }
}
fun printParam() {
print("Param: $param")
}
}
Usage:
MySingleton.getInstance("something").printParam()
There are also two native Kotlin injection libraries that are quite easy to use, and have other forms of singletons including per thread, key based, etc. Not sure if is in context of your question, but here are links to both:
Injekt (mine, I'm the author): https://github.com/kohesive/injekt
Kodein (similar to Injekt): https://github.com/SalomonBrys/Kodein
Typically in Android people are using a library like this, or Dagger, et al to accomplish parameterizing singletons, scoping them, etc.
I recommend that you use this form to pass arguments in a singleton in Kotlin debit that the object your constructor is deprived and blocked:
object Singleton {
fun instance(context: Context): Singleton {
return this
}
fun SaveData() {}
}
and you call it this way in the activity
Singleton.instance(this).SaveData()
If you looking for a base SingletonHolder class with more than one argument. I had created the SingletonHolder generic class, which supports to create only one instance of the singleton class with one argument, two arguments, and three arguments.
link Github of the base class here
Non-argument (default of Kotlin):
object AppRepository
One argument (from an example code in the above link):
class AppRepository private constructor(private val db: Database) {
companion object : SingleArgSingletonHolder<AppRepository, Database>(::AppRepository)
}
// Use
val appRepository = AppRepository.getInstance(db)
Two arguments:
class AppRepository private constructor(private val db: Database, private val apiService: ApiService) {
companion object : PairArgsSingletonHolder<AppRepository, Database, ApiService>(::AppRepository)
}
// Use
val appRepository = AppRepository.getInstance(db, apiService)
Three arguments:
class AppRepository private constructor(
private val db: Database,
private val apiService: ApiService,
private val storage : Storage
) {
companion object : TripleArgsSingletonHolder<AppRepository, Database, ApiService, Storage>(::AppRepository)
}
// Use
val appRepository = AppRepository.getInstance(db, apiService, storage)
More than 3 arguments:
To implement this case, I suggest creating a config object to pass to the singleton constructor.