Kotlin — Singleton Pattern

Enes Okurterzi
5 min readJun 24, 2024

--

Hello everyone, in this article, we will define the singleton pattern, look at the solutions to the problems we may encounter in asynchronous operations, and finally touch on the points we need to pay attention to. In the article, we will make a review by comparing Java and Kotlin codes.

When we aim for flexibility and efficiency or an effective design during the software design phase, we encounter design patterns.

Today we will talk about the singleton pattern, one of these design patterns. So what is this singleton pattern:

Singleton pattern: Contains a single class responsible for creating an object, ensuring that only a single object is created. This class provides a direct path to access only a single object of the class, without needing to create the object of the class.

Now we have learned the definition, let’s see how what we have learned looks like in practice. In this article, I will first explain it in Java because the Kotlin side does this job very simply, so I think it makes sense to see the Java side first to understand its logic.

public class SingleObject {

private static final SingleObject instance = new SingleObject();

private SingleObject(){}

public static SingleObject getInstance() {
return instance;
}
}

Let’s approach the class we wrote step by step. First, we made the constructor private and then we created the example as “private static final” because this variable is intended to be specified (static) and shown once without creating a class object within a static function (private), which only wants to be accessed from within the class and not to be changed again. We want (final) and finally, we provide access to our example with the “getInstance” function.

We can create a singleton class in Java in different ways. You can take a look at some of them at this link.

We said that the Kotlin side is different, but if you want to know why this difference arises, let’s give an example for it:

object SingleObject {

}

All of the codes we have written are covered with the object keyword in Kotlin. Of course, we will not leave it at that and decompile our code from byteCode to Java:

public final class SingleObject {
@NotNull
public static final SingleObject INSTANCE = new SingleObject();

private SingleObject() {
}
}

Here, we perform the same operations as above, with the only difference being that access to the variable is made public. Of course, there are various reasons for this, we will not question those details. It will be enough for us to interpret the code we have seen and observe that a single object keyword corresponds to these operations.

We have understood the singleton pattern, we may encounter various problems in asynchronous operations when using this pattern. One of these can be said to be different threads using the same variable. We can use the volatile keyword to solve this problem.

Volatile: It is used to notify other threads that use these variables about changes made on variables.

Let’s give examples for immediate use:

public class SingleObject {

private static final SingleObject instance = new SingleObject();
private static volatile int exampleNumber = 0;

private SingleObject(){}

public static int getExampleNumber() {
return exampleNumber;
}

public static void setExampleNumber(int exampleNumber) {
SingleObject.exampleNumber = exampleNumber;
}

public static SingleObject getInstance() {
return instance;
}
}

As you can see, when creating the variable in Java, we use this keyword by typing “volatile” after the visibility modifier and static keyword. So how do we do this in Kotlin? Let’s create an example right away:

object SingleObject {
@Volatile
var exampleNumber: Int = 0
}

As you can see, we can use this keyword with the “volatile” annotation in Kotlin. Of course, we will not leave it like this and see how it looks in Java:

public final class SingleObject {
@NotNull
public static final SingleObject INSTANCE = new SingleObject();
private static volatile int exampleNumber;

private SingleObject() {
}

public final int getExampleNumber() {
return exampleNumber;
}

public final void setExampleNumber(int var1) {
exampleNumber = var1;
}
}

As we expected, it turns into exactly what we wrote in Java.

Another problem is that more than one thread can access a function at the same time. The synchronized keyword can be used to solve this problem.

Synchronized: It ensures that the threads accessing the function for which it was written do so sequentially.

Let’s give examples for the use of this keyword:

public class SingleObject {

private static final SingleObject instance = new SingleObject();

private SingleObject(){}

public static SingleObject getInstance() {
return instance;
}

public static synchronized void exampleFunc() {

}
}

As you can see, when creating the function in Java, we use this keyword by typing “synchronized” after the visibility modifier and static keyword. So how do we do this in Kotlin? Let’s create an example right away:

object SingleObject {
@Synchronized
fun exampleFunc() {}
}

As you can see, we can use this keyword with the “synchronized” annotation in Kotlin. Of course, we will not leave it here and see how it looks in Java:

public final class SingleObject {
@NotNull
public static final SingleObject INSTANCE = new SingleObject();

private SingleObject() {
}

public final synchronized void exampleFunc() {
}
}

As we expected, this turns into exactly what we wrote in Java.

In this way, we have seen the solutions to the problems we may encounter in asynchronous operations.

Situations to Consider

In some cases, when using singleton patterns, we may encounter undesirable results. I think knowing these problems before using the singleton pattern will allow us to use it more effectively. If you wish, let’s look at these problems immediately:

  • When singleton pattern is used too much, it creates the risk of memory leak due to the proliferation of objects that we do not use in memory.
  • The use of singleton pattern can be done in some cases to avoid making frequent requests to the backend. If the synchronization of changes made to the object used is not taken into consideration, differences may occur between the data in the database and the data in our application.

In this way, we have examined the answers to the questions what is the singleton pattern, how to use it, how to prevent the problems we may encounter in asynchronous operations, and what we should pay attention to when using this pattern.

Thank you for taking the time to read my article. If you have any feedback or questions about my content, you can contact me on LinkedIn. Stay healthy and happy!

--

--