Singleton

The singleton pattern is used when we want to be sure that only one instance of a certain class is ever created. According to this pattern, a class is responsible for providing a reference to the same underlying instance. The following example shows the easiest way to implement the singleton pattern in Java:

public class Singleton {

     private Singleton() {}
private static final Singleton INSTANCE = new Singleton(); public static Singleton getInstance() { return INSTANCE; } }

However, Kotlin supports the concept of object declaration. For instance, we need to create an object that represents a user who is currently logged in:

object User {
var firstName: String? = null
var lastName: String? = null
}

The version of this code that is decompiled to Java looks like this:

public final class User {
@JvmField
@Nullable
public static String firstName;
@JvmField
@Nullable
public static String lastName;
public static final User INSTANCE;

static {
User var0 = new User();
INSTANCE = var0;
}
}

As you can see, object declaration looks like the easiest version in Java under the hood. However, this approach can be inefficient because it immediately creates an instance of the class when the application starts, even if it is not needed. To solve this problem, we can use the lazy-loading singleton, which may look like this:

public class Singleton {
  private Singleton() {}
  private static JavaSingleton INSTANCE;

  public static JavaSingleton getInstance() throws Throwable {
      if (INSTANCE == null) {
          INSTANCE = new JavaSingleton();
      }
      return INSTANCE;
  }
}

Since we can declare variables as first-class citizens in Kotlin, we can use the following implementation:

class User private constructor(
var firstName: String? = null,
var lastName: String? = null
) {
companion object {
private val user by lazy(LazyThreadSafetyMode.NONE) {User()}
fun getInstance(): User = user
}
}

fun main(args: Array<String>) {
with(User.getInstance()) {
firstName = "Ihor"
lastName = "Kucherenko"
}
}

In the preceding example, we use the lazy delegate , which takes an instance of the LazyThreadSafetyMode enum and a lambda that invokes only once when we refer to the user variable for the first time.

However, in a multithreaded environment, the getInstance() function can return more than one instance of the singleton because this function is not protected by synchronization. To deal with this scenario, we can use the double-checked locking synchronization approach:

public class Singleton {
     private Singleton() {}
     private static JavaSingleton INSTANCE ;
     public static JavaSingleton getInstance() {
        if (INSTANCE == null){
            synchronized(JavaSingleton.class){
               if(INSTANCE == null){
                    INSTANCE = new JavaSingleton;
               }
            }
            return INSTANCE ;
        }
     }
}

In Kotlin, we can use the following declaration:

private val user by lazy {User()}

In this case, an instance of the SynchronizedLazyImpl class has the double-checked locking technique under the hood:

public actual fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)

We know that singleton is an anti-pattern. These are global variables that can be changed at any time, which is why they are enemies of encapsulation; it's difficult to define the state and behavior of an instance that depends on a singleton. In addition, the singleton pattern breaks the single responsibility principle because one class is responsible for creating this class and keeping only one instance at the same time.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
13.59.32.1