Explain Codes LogoExplain Codes Logo

Singleton with Arguments in Java

java
singleton
thread-safety
builder-pattern
Nikita BarsukovbyNikita Barsukov·Aug 11, 2024
TLDR

Implement a Singleton with arguments by utilizing the Initialization-on-demand holder idiom, with parameters encapsulated in a config object. This results in thread-safe and lazy instantiation with the required arguments.

public class SingletonConfig { private String value; public SingletonConfig(String value) { this.value = value; } // create getters for value } public class MySingleton { private SingletonConfig config; private MySingleton(SingletonConfig config) { this.config = config; } private static class Holder { // This instantiates the singleton object. private static MySingleton instance(SingletonConfig config) { return new MySingleton(config); } } public static MySingleton getInstance(SingletonConfig config) { return Holder.instance(config); } }

Initialize the Singleton with a SingletonConfig instance containing your parameters. To maintain the single instance behavior, re-call getInstance(null) following the initial creation.

SingletonConfig config = new SingletonConfig("parameter"); // Could be your pet's name as well MySingleton singleton = MySingleton.getInstance(config);

Ideal for a one-time configuration setup. Might upset your Singleton if used interchangeably during runtime.

Enforcing Singleton uniqueness

Have a check in the getInstance method to prevent re-initialization of the Singleton. This means, if different parameters attempt to re-initialize the Singleton, it will return the original configuration or raise an exception, stating "Sorry, I don't do reinitalization".

public static MySingleton getInstance(SingletonConfig config) { if(Holder.singleton != null && config != null) { throw new IllegalStateException("Backoff!! Already initialized me with original config"); } return Holder.singleton == null ? Holder.instance(config) : Holder.singleton; }

Thread safety and state immutability

To ensure that the Singleton is thread-safe, make it immutable post-initialization. The use of final fields within the Singleton makes sure the state remains unaltered after creation. This restricts any nefarious activities in multi-threaded environment.

Singleton creation with Builder pattern

In cases where the Singleton requires an elaborate initialization with multiple parameters, employ the Builder pattern. The builder can have a built-in method to check for previous Singleton initialization and will throw a party, I mean, an exception if there is an attempt to reinitialize it.

Instance management with Factory pattern

To manage parameters resulting in various types of Singletons, look into the Factory pattern. It feels like a name hash for a music band but instead is a hash table storing Singleton instances by name and making sure each named instance maintains its singleton status.

Enumerated Singleton instances

If the Singleton configurations are limited, Enum can effectively handle the parameterized singletons. Enums ensure the single instance scenario and can represent different configurations in a type-safe and effective manner.

Managing large objects with distributed caching

In situations with large-scale systems or large shared objects, consider using distributed caching solutions like Hazelcast or Apache Ignite. This feels like the Singleton having a mansion to live in, across the entire computing grid.

Logger instance management through Singleton

A standard usage of Singleton pattern can be seen in logging frameworks. A LoggerFactory can have a getLogger(String name) method which returns a logger singleton for the specific name. It either ignores the passed parameters or uses it to configure the logger, but always returns the respective Singleton instance.

Avoiding Singleton mix-ups across JVM boundaries

Remember, just as a person existing at one location doesn't mean they exist everywhere, similarly, a Singleton in one JVM doesn't equate to being singleton across multiple JVMs. In such cases, you need an external system for coordination, probably through a database or filesystem lock.