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