DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Languages
  4. Beautiful Constructors Look Like This

Beautiful Constructors Look Like This

A deep dive into the world of Java Constructors and how to write them appropriately.

Sam Atkinson user avatar by
Sam Atkinson
·
Apr. 20, 16 · Tutorial
Like (27)
Save
Tweet
Share
20.08K Views

Join the DZone community and get the full member experience.

Join For Free

Constructors are great. As I’ve said before, I’m deadly against setters in code. We should be aiming for immutability in our applications wherever possible, and setters are the nemesis of this. Which means that we should be aiming to set everything up in the constructor.

Furthermore, this is the only thing that the constructor should be doing. Do not place any complex logic into the constructor of your object. It should be a place purely for setting the fields up of your object. Anything else that needs to be done — connections to other services for example — should always be part of some sort of init method. Preferably, the init method should have no parameters and everything needed to get the object into the correct state should be passed in as part of the constructor.

public class DoesSomeComplexConnection {

private final String dbUrl;
private final String user;

public DoesSomeComplexConnection(String dbUrl, String user){
    this.dbUrl = dbUrl;
    this.user = user;
}

public void init(){
    connectToDb(dbUrl, user);
}

As a rule, aim to have a solitary constructor. This makes understanding the code much easier. Your objects should not have “optional” dependencies- Your object should have a set of functionality which is always executed and relies on a certain set of dependencies. If you, the caller, do not need that functionality, then pass in no-op functionality.

    public DoesSomeComplexConnection(String dbUrl, String user, Notifier notifyOnConnection){
    this.dbUrl = dbUrl;
    this.user = user;
    this.notifyOnConnection = notifyOnConnection;
}

public void init(){
    connectToDb(dbUrl, user);
    notifyOnConnection.connected();
}

 public static void main(String[] args) {
    new DoesSomeComplexConnection(dbUrl, user, new DoNothingOnNotification())

}

private static class DoNothingOnNotification implements Notifier{
    @Override
    public void connected() {
    }
}

private interface Notifier {
    void connected();
}

This can be made even clearer in Java 8 with an anonymous no-op function.

        new DoesSomeComplexConnection(dbUrl, user, () -> {});

One of the most important parts of your code, which I will go into in a seperate article, is that you should never pass null around. Friends don’t let friends use null. By establishing this simple rule on your codebase means you can remove an awful lot of null checks that simply don’t need to exist.

If your codebase is irreparable, then put your null checks in the constructor. Make sure that by the end of construction all your internal fields are set and your object is complete.

Sometimes the answer to this is viewed as multiple constructors, which provide default implementations. Multiple constructors really clutter up the code base and make understanding difficult as mentioned before. It’s absolutely fine however to make default implementations available for the end user, like so:

         public class DoesSomeComplexConnection {

             public static final Notifier NO_OP_NOTIFIER = () -> {};
                …
                }

        new DoesSomeComplexConnection(dbUrl, user, DoesSomeComplexConnection.NO_OP_NOTIFIER);

A Practical Example:

Let’s take a look at the code for Reader from swagger-core:

public class Reader { 
public Reader(Swagger swagger) { 
this(swagger, null); 
}
public Reader(Swagger swagger, ReaderConfig config) {
    this.swagger = swagger == null ? new Swagger() : swagger;
    this.config = new DefaultReaderConfig(config);
}
}


public class DefaultReaderConfig implements ReaderConfig {
     /**
 * Creates default configuration.
 */
  public DefaultReaderConfig() {
}

/**
 * Creates a copy of passed configuration.
 */
public DefaultReaderConfig(ReaderConfig src) {
    if (src == null) {
        return;
    }
    setScanAllResources(src.isScanAllResources());
    setIgnoredRoutes(src.getIgnoredRoutes());
}

This code is particularly heinous for a number of reasons: 

  • Nulls are being thrownn around left right and center. This makes it difficult to comprehend what’s going on and results in extra code for null checks. 

  • Multiple constructors, particularly when the first is just passing another null!

  • Construction of another Object in the constructor- this isn’t that big a sin, and is one that you need to apply common sense to. However, as it’s only accepting the one parameter it’s effectively decorating. Why not pass the required object in? 

  • In likelihood, it is passing the required object in. But they’re wrapping it again just to be safe.

First of all, we can delete the first constructor from reader. Anyone requiring it is brave enough to pass the null in themselves (although we’ll be getting rid of nulls in due course).

Second, if we’re comfortable accepting people shouldn’t pass null around, we can remove the Swagger null check. If this is a problem the application will explode quite quickly and we can fix any other dependent code. This takes us to:

public class Reader {

public Reader(Swagger swagger, ReaderConfig config) {
    this.swagger = swagger;
    this.config = new DefaultReaderConfig(config);
}
}

Ok, getting better. Now, there is literally no need for the wrap call, it’s purely there as a null check again. DefaultReaderConfig is the only implementation available anyway. But let’s assume that’s not the case; we should change this code so that the object is passed directly in, not constructed internally:

    public class Reader {

public Reader(Swagger swagger, DefaultReaderConfig config) {
    this.swagger = swagger;
    this.config = config;
}
}

Ok, so our code is looking much cleaner here. What about if someone doesn’t have a config to pass through (e.g., would have been null before)? Default implementation.

public class Reader {
    public static final DefaultReaderConfig DEFAULT_CONFIG = new DefaultReaderConfig()

public Reader(Swagger swagger, DefaultReaderConfig config) {
    this.swagger = swagger;
    this.config = config;
}
}

//Someone using this class
new Reader(swagger, Reader.DEFAULT_CONFIG)

As a result of this, we can get rid of the 2 constructors in ReaderConfig too, as this was their only use.

Much cleaner and easy to understand code all round.

Object (computer science)

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Steel Threads Are a Technique That Will Make You a Better Engineer
  • Tracking Software Architecture Decisions
  • DevOps for Developers: Continuous Integration, GitHub Actions, and Sonar Cloud
  • How To Choose the Right Streaming Database

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: