This is the most popular single guy in the neighborhood. Everybody knows him, everybody talks about him, and anybody can find him easily. Even people who will frown when other design patterns are mentioned will know it by name. At some point, it was even proclaimed an anti-pattern, but only because of its wide popularity. So, for those who are hearing about it for the first time, what is this pattern about? Usually, if you have an object, you can create as many of its instances as you want.
Say, for example, you have the Cat class:
class Cat
You can produce as many of its instances (cats, to be precise), as you want:
val firstCat = Cat()
val secondCat = Cat()
val yetAnotherCat = Cat()
And there's no problem with that. What if we wanted to disallow such behavior? Clearly, we have to create an object in some way for the first time. But from the second time on, we need to recognize that this object was initialized once already, and returns its instance instead. That's the main idea behind being a Singleton. In Java and some other languages, this task is quite complex. It's not enough to simply make the constructor private and remember that the object was initialized at least once already. We also need to prevent race conditions, where two separate threads try to initialize it exactly at the same time. If we allowed that, it would break the entire concept of a Singleton, as two threads would hold references to two instances of the same object. Solving this problem in Java requires doing one of the following: Accepting that a Singleton will initialize eagerly when your application starts, and not when it is first accessed Writing some smart code to prevent such race conditions and still stay performant Using a framework that already solves it Kotlin just introduces a reserved keyword for that. Behold, an object as follows:
object MySingelton{}
You don't need curly brackets there. They're just for visual consistency.
This combines declaration and initialization in one keyword. From now on, SingletonAnalysisWithJavacan be accessed from anywhere in your code, and there'll be exactly one instance of it. Of course, this object doesn't do anything interesting. Let's make it count the number of invocations instead:
import java.util.concurrent.atomic.AtomicInteger
object SingletonAnalysisWithJava {
private val counter = AtomicInteger(0)
fun increment() = println(counter.incrementAndGet())
}
We won't test it for thread safety yet Threads and Coroutines. For now, we test it only to see how we
call our Singleton: for (i in 1..10) {
println(CounterSingleton.increment())
}
fun main(args: Array<String>) {
// Singleton test
for (i in 1..10){
SingletonAnalysisWithJava.increment()
}
}
This will print numbers between 1 and 10, as expected. As you can see, we don't need the getInstance() method at all. The object keyword is used for more than just creating Singletons. We'll discuss it in depth later. Objects can't have constructors. If you want some kind of initialization logic for your Singleton, such as loading data from the database or over the network for the first time, you can use the init block instead:
object CounterSingleton {
init {
println("I was accessed for the first time")
}
// More code goes here
}
It is also demonstrated that Singletons in Kotlin are initialized lazily, and not eagerly, as some could suspect from the ease of their declaration. Just like regular classes, objects can extend other classes and implement interfaces.
1 comment:
Very well explained
Post a Comment