Constructors are your first opportunity to make sure your custom types are well formed. This episode explains the different kinds of constructors and how you should use them.
The first thing to understand is that constructors are just methods in your class that have some special rules. And you don’t normally call constructors directly.
Your classes can have multiple constructor methods depending on different uses or how easy you want to make it for other code to create instances.
One special constructor is called the default constructor. You’ll use this when you want to be able to construct instances without needing to specify any additional information. This is really just a constructor that takes no parameters. There can only be one default constructor per class.
Another special constructor is called the copy constructor. There can also only be a single copy constructor per class. This is used whenever you want to create a new instance that is a copy of some other variable of the same type. You write this method to take a single parameter of the same type. Usually this will be a constant reference to the other instance.
Other constructors will be whatever you need and will have different parameters.
Listen to the full episode or read the full transcript below.
Let me start by saying that constructors are just methods that have some special rules. These rules vary from one language to another and because I don’t know all languages, some of this may not apply to your language. These fundamental concepts I’m going to describe should help you to better understand constructors in any language.
The first rule is that you can’t just call a constructor anytime you want like other methods. The previous episode introduced classes as types. While you can work with a type directly, you’ll almost always be working with instances of types. A constructor helps you create these instances.
When you declare a variable such as a float, you just provide the type, which is float, a name that you choose, and optionally, an initial value. You now have an instance of that float. That means you have some real memory dedicated to just this one float that’s identified by whatever name you gave to your variable.
But what if you wanted a special kind of float that could never have negative values? You’ll need your own class for this special purpose float. And you’ll want to write methods that handle the initial construction of instances of your class to make sure they’re protected from the very beginning.
Or maybe you have a more complicated example in mind. Maybe you need a class to represent a character in an adventure game. This class will need lots of data of different types and all this data will need to be coordinated to make sure it all makes sense together. You’re going to want your character instances to be valid from the very beginning.
The alternative is to allow a new instance of your class to be created that cannot or should not be used until certain properties have been set. In other words, certain data needs to be set first. What that is is up to you. Do you really want to have to call a series of methods to set this information? Or do you want to make a separate call to some initialization method? What if you’re creating a whole bunch of these classes? You may not always be able to properly initialize each one.
This is the job of constructors. Your classes can have multiple constructor methods depending on different uses or how easy you want to make it for other code to create instances.
So what does a constructor look like? Most languages require constructors to have no return type. I’m not talking about a void return type here. This is an exception to the rule that all methods need a return type. The reason is because you don’t really call constructors like normal methods. The compiler will call the constructor for you whenever you need to create an instance of your class.
Many languages require the constructor methods to have the same name as your class. But some languages have fixed names for constructors.
One special constructor is called the default constructor. You’ll use this when you want to be able to construct instances without needing to specify any additional information. This is really just a constructor that takes no parameters. There can only be one default constructor per class. You write this method to put your instance into a good state that makes sense. You might use this to set number data types to zero, string types to empty, bool types to false, etc. Some languages like C++ will provide a default constructor for you if you don’t write any other constructors. As long as you have a default constructor that either you wrote yourself or that the compiler provided, you will then be able to create instances of your classes with just your class type and a name for your variable.
Another special constructor is called the copy constructor. There can also only be a single copy constructor per class. This is used whenever you want to create a new instance that is a copy of some other variable of the same type. You write this method to take a single parameter of the same type. Usually this will be a constant reference to the other instance. In my code, I like to follow a normal practice of naming this parameter src which stands for source.
Other constructors will be whatever you need and will have different parameters. Let’s say your class has 3 integers as data. You might want to have one constructor that takes 3 integer parameters so that other code can construct instances and specify all three values. This doesn’t mean that you have to accept all three values. That’s the whole point of constructors. You get to decide if the 3 values provided make sense together and if so, then you can proceed and return a new instance of your class. If not, then you get the chance to inform the caller of the error.
Continuing this example, you might then want another constructor that only needs 2 integers. What about the third? The class still has that information. A good practice to follow is to write this constructor so that it picks a default value for the other data and then just calls the constructor with all 3 parameters instead. This is the closest you’ll normally get to calling a constructor directly and that’s because you’re already writing the code for another constructor. Having one constructor call another one is called chaining your constructors and has some good benefits.
The biggest benefit is all the code you want to verify if all the parameters look good can reside in just one place. You don’t need to repeat the same code in multiple constructors. And repeating code is bad. It’s not only extra typing which goes against the general laziness of programmers but more code means more chances of bugs.
Having code in one place makes your code easier to understand.
And it’s also faster to change when needed because you only need to change one place.
There’s one more concept I want to explain while we’re talking about constructors and that’s immutable objects. Sometimes, I’ll refer to an instance of a class as an object. This has always worked well for me although there might be some languages where the terms mean something different.
Back to immutable objects. These are instances that cannot change once they are constructed. Taking a very simple example, let’s say you have an immutable integer. You can construct a variable of this immutable int type but you need to give it a value right away. The constructor will change the value once during construction and then that’s it. Your variable is not very variable anymore.
That was a silly example. But there are valid reasons for why you might want immutable objects. Because they don’t change, they’re simpler to work with. If you want to change one, you really end up creating a new instance with a new value.
Immutable objects are great for collections that use the object data to find instances within the collection. Imagine if you created one of the adventure game character objects and put it in a collection and used the name of the character to later be able to find the instance. Now some other code gets this instance and decides to change the name. And you can no longer find your character in the collection. That’s bad. If it had been immutable, then this would not have happened. You can design your constructors so that you can only initialize your data once during construction.
Don’t get me wrong, I’m not saying that all types should be immutable. Just that if you do want a type to be immutable, then your constructors will be very important.
Actually, scratch that. Constructors are always important. No matter how your classes are designed, your constructors are the first entry method for all your instances.