Constructors
Constructors are public instance methods of a class used to initialize the instance variables. A constructor takes the same name as the class and does not have a return type(not even void). The constructor is called following the keyword, new, at the time of instantiation.
Default constructors for objects are created by Java. A default constructor takes no parameters. In a Java supplied default constructor, all Strings are set to "" and all numbers are set to 0. Once we specify any other constructor, we must also explicitly specify our default constructor. Using a default constructor, however, means that we then have to set each variables using its setter, if available.
We often overload a constructor to allow us to send in some, all, or no values for the instance variables. The key to overloading a constructor is that each version must have a different number/type of parameters.
So, our Dog class, with overloaded constructors may look as follows:
public Dog() //default
{
this.name="";
this.cage=0;
this.sound="bark";
this.customer=null;
}
public Dog(String name)
{
this.name=name;
this.cage=0;
this.sound="bark";
this.customer=null;
}
If we were to overload the constructor several more times, we would quickly begin to find the process tedious, as each constructor requires four lines of code to initialize each variable. If we extrapolate that to a larger class with say, 10 varibles, and wrote ten constructors, that would be one hundred lines of code just for constructors. So, the faster way to implement overloaded constructors requires writing a constructor that takes all of the values as parameters and using the keyword
this to call it. Similar to the way
this.name refers to the instance variable name,
this by itself refers to the instance constructor.
public Dog(String name, int cage, String sound, Customer customer){
this.name=name;
this.cage=cage;
this.sound=sound;
this.customer=customer;
}
public Dog() //default{
this("", 0, "bark", null); //calls the full constructor and sends in default values
}
public Dog(String name){
this(name, 0, "bark", null); //calls the full constructor with a mixture of default and parameter values
}
Our next issue is error checking. In writing setters we were so careful to ensure that the incoming data was valid, thus eliminating the need to test it throughout the rest of the program. We could add error checking to the constructors as well, but that would require copying and pasting code (which means that there must be a better way). So, if we take the full constructor and call the setters (that already have the error checking) we can use the result to decide if it needs a default value. Look at the following example:
public Dog(String name, int cage, String sound, Customer customer){
this.name=name;
if(!setCage(cage)) cage=0;
this.sound=sound;
this.customer=customer;
}
public Dog() //default{
this("", 0, "bark", null); //calls the full constructor and sends in default values
}
public Dog(String name){
this(name, 0, "bark", null); //calls the full constructor with a mixture of default and parameter values
}
It is important to note that the job of a constructor is to initialize the values of EVERY instance variable. So, if the setter fails, the constructor MUST assign the variable to a default value.
The toString Method
Similar to the problem of writing multiple lines of code each time we want to create an object, we currently have to write just as many lines of code to output an object, each time calling the appropriate getter. In a program that outputs an object with 5 variables ten times, that is 50 lines of code. Obviously, we could write a method in the program that would take in the object and output it to be more efficient. Even better is to write the output method INSIDE the object itself. That way, any program that uses the object will have one line access to outputting the information. The other benefit is that since it is an instance method, it has direct access to the variables without the need for getters.
By default, every object has a toString method and that method is called when the object is output using System.out.print(). So, the following lines of code are equivalent:
System.out.println(obj.toString());
AND
System.out.println(obj);
Unfortunately, the default output for an object is its address in memory. To change this behavior, we must
override the toString method. Overriding a method means to write the method exactly as it is written in its parent class (more about this in Inheritance). So, when writing a toString method, we must always use the following method signature:
public String toString()
Here is an example of the Dog class toString method:
public String toString(){
return "Name:"+name+"\nCage:"+cage+"\nSound"+sound+"\nCustomer:"+customer;
}
Similar to how a constructor is required to set all of the instance variables, a toString method should show all of the instance variables. This is called the
state of the object.