DEV Community

Kyle Carter
Kyle Carter

Posted on • Updated on • Originally published at blog.scaledcode.com

Effective Java Tuesday! Singletons!

Time for chapter three of our Effective Java review. Today's is a fairly simple one. Today we are talking about the singleton pattern. The singleton pattern is quite well known and basically comes down to an object that only allows instantiation once.

So what benefits does a singleton give us?

  • Expensive objects can be avoided being generated multiple times.
  • If there is a reason we shouldn't have an unbounded amount of objects we can own instance control.

What about some cons:

  • They are extremely hard to test (having your singleton implement an interface can help this some)
  • They basically serve as global state that can be complex and cause bugs.

So a singleton is definitely not without it's costs but let's say we have determined that we do need a singleton, what are our options of implementing the pattern? Well Effective Java goes through three methods all including hiding the constructor of the object in one way or another.

Option 1: public constant field

The first option that is described is quite simple. The first step is to create a private constructor. After that you simply create a public static final field that instantiates the object to be used. Let's look at an example:

public class CEO {
  public static final INSTANCE = new CEO();
  private CEO() { }
  public void fire(Employee slacker) { ... }
}
Enter fullscreen mode Exit fullscreen mode

So in this example we want to make sure that we don't end up with more than one CEO and so we make a singleton out of class. Then when a user wants to gain access to the CEO they simple call CEO.INSTANCE. This method is definitely very easy to write and very easy to use. The downsides are that users can get around the single instance protection by using some reflection skills. It also doesn't give as much control over instance creation and going back on the singleton choice.

Option 2: getInstance factory method

The second option is what I am most familiar with. This method is built around having a static factory getInstance factory method method that provides the single instance of the class. Example time:


public class CEO {
  private static final INSTANCE = new CEO();
  private CEO() { }
  public static CEO getInstance() {
    return INSTANCE;
  }
  public void fire(Employee slacker) { ... }
}
Enter fullscreen mode Exit fullscreen mode

So what benefits do we get here? Well for one it's very recognizable as a singleton so the user can easily see what they are interacting with. It also allows us more control over our instance creation. For example we can delay instantiation of the object until it's first needed.

public CEO {
  private static final INSTANCE;
  public static synchronized CEO getInstance() {
     if(INSTANCE == null) {
        INSTANCE = new CEO();
     }
     return INSTANCE;
  }
}
Enter fullscreen mode Exit fullscreen mode

With a pattern like this you need to be careful about race conditions though. It is of note that this pattern is still susceptible to reflection attacks. Effective Java also goes into how to handle serialization as it relates to singletons and is not extremely straightforward. I've never had to deal with that so I won't tackle it here but if you need to go look it up.

Option 3: Single Element Enum

The final option is to create a single element enum. This was not an option that I had ever seen and it does still feel a little unnatural to me. So let's look at an example:

public enum CEO {
  INSTANCE;

  public void fire(Employee slacker) { ... }
}
Enter fullscreen mode Exit fullscreen mode

So what does this give us? Well it is simple to write. Probably the least code of the three options. It also is very safe. It doesn't leave the option for reflection attacks. It also handles the serialization complexities that I skipped above. It does make it so you can't extend from a class but you can still implement interfaces. Effective Java pitches that this is the option that most people should take. Given that I have never personally seen someone use this method it's quite interesting to me that it is pitched as the option most should take.

How about you? Have you ever seen this method used?

Top comments (14)

Collapse
 
jboschmans profile image
jboschmans

I know I'm late to the party, but I wonder why you would ever use the Singleton pattern instead of just a static class. Doesn't it achieve exactly the same?

Collapse
 
kylec32 profile image
Kyle Carter

I'm not sure I'm following what you mean by "static class." The only static classes come to mind are static inner classes (dev.to/kylec32/effective-java-favo...) which don't solve the singleton problem but instead allows access to a nested class without having a handle on the outer class. Still useful, just not for this problem. Maybe I'm just misunderstanding the question thought.

Collapse
 
jboschmans profile image
jboschmans

Thank you for your response. I guess I didn't explain very well. What I mean is a class where the members and methods are static. This way you can call the static method of the class to do what you want instead of creating an object. eg: ClassName.staticMethod()

Thread Thread
 
kylec32 profile image
Kyle Carter

Thanks for that clarification. Yeah I don't see any problem with that. It seems like just a slight change from option 1. Potentially the downside I see is you don't keep the option of changing how it gets initialized in the future. If that's not needed then you likely are good with this option.

Thread Thread
 
jboschmans profile image
jboschmans

Thank you for your answer!

Collapse
 
raipc profile image
Anton Rybochkin

What about storing the singleton value in a static nested class?

Collapse
 
kylec32 profile image
Kyle Carter

Interesting. I see no reason that you couldn't do that if it made sense to put the singleton in such a nested class. Something to keep in mind if the option ever comes up.

Collapse
 
raipc profile image
Anton Rybochkin

I thought that was the official way to create lazy singletons without synchronization.

public class CEO {
  private CEO() { }

  public static CEO getInstance() {
    return InstanceHolder.instance;
  }

  public void fire(Employee slacker) { ... }

  private static final class InstanceHolder {
    private static final CEO instance = new CEO();
  }
}
Thread Thread
 
kylec32 profile image
Kyle Carter

Will you look at that! Still never seen that but you are 100% correct this would allow lazy initialization without synchronization. Thanks for educating me about that.

Collapse
 
itsraghz profile image
Raghavan alias Saravanan Muthu

Wow.. this is pretty awesome series. Gonna bookmark all the posts in this thread/series and start grasping them gradually. Thank you Kyle.

Collapse
 
rossholloway94 profile image
Ross Holloway

"They are extremely hard to test" - why?

Collapse
 
kylec32 profile image
Kyle Carter • Edited

A fair question indeed, and one that I did not demonstrate above in my examples. Since the constructor is not accessible to us, even in a test, we are unable to change how the object is created and can't get the benefit of dependency injection (dev.to/kylec32/effective-java-tues...). While none of the above examples show any places where that would be a problem, many singletons in the wild do have this problem if they have any external dependencies (DB, network, other components, etc). So the more I think of it, it's likely the lack of ability to use dependency injection that is the true problem here.

Compare that to a singleton that is just a collection of pure functions that would indeed be testable (but at that point you are more looking at a Utility class dev.to/kylec32/effective-java-tues...)

Collapse
 
rossholloway94 profile image
Ross Holloway • Edited

I think you're right about Dependency Injection for unit testing with only, say, JUnit 5.

However, this is where I have found mocking libraries such as Mockito benefit the tests, as the coupled logic can be stubbed out.

(Thanks for the detailed reply 👍 )

Edit: Your second link doesn't appear to be working?

Collapse
 
ehimsi profile image
Himanshu Joshi

Per Effective Java - we get the serialization machinery free.