Customizing constructors and initialization

We want to initialize instances of the Rectangle class with the width and height values for the new rectangle. In order to do so, we can take advantage of the previously introduced constructors. Constructors are special class methods that are automatically executed when we create an instance of a given type. Java runs the code within the constructor before any other code within a class.

We can define a constructor that receives both the width and height values as arguments, and use it to initialize the fields with the same names. We can define as many constructors as we want to, and therefore, we can provide many different ways of initializing a class. In this case, we just need one constructor.

The following lines create a Rectangle class and define a constructor within the class body. At this time, we aren't using access modifiers at all because we want to keep the class declaration as simple as possible. We will work with them later. The code file for the sample is included in the java_9_oop_chapter_03_01 folder, in the example03_03.java file.

class Rectangle {
    double width;
    double height;

    Rectangle(double width, double height) {
        System.out.printf("Initializing a new Rectangle instance\n");
        System.out.printf("Width: %.2f, Height: %.2f\n", 
            width, height);
        this.width = width;
        this.height = height;
    }
}

The constructor is a class method that uses the same name as the class: Rectangle. In our sample Rectangle class, the constructor receives two arguments of the double type: width and height. The code within the constructor prints a message indicating that the code is initializing a new Rectangle instance and prints the values for the width and height. This way, we will understand when the code within the constructor is executed. Because the constructor has an argument, it is known as a parameterized constructor.

Then, the following line assigns the width double value received as an argument to the width double field. We use this.width to access the width field for the instance and width to reference the argument. The this keyword provides access to the instance that has been created and we want to initialize, that is, the object that is being built. We use this.height to access the height field for the instance and height to reference the argument.

The two lines before the constructor declare the width and height double field. These two fields are member variables that we can access without restrictions after the constructor finishes its execution.

The following lines create four instances of the Rectangle class named rectangle1, rectangle2, rectangle3, and rectangle4. The code file for the sample is included in the java_9_oop_chapter_03_01 folder, in the example03_04.java file.

Rectangle rectangle1 = new Rectangle(31.0, 21.0);
Rectangle rectangle2 = new Rectangle(182.0, 32.0);
Rectangle rectangle3 = new Rectangle(203.0, 23.0);
Rectangle rectangle4 = new Rectangle(404.0, 14.0);

Each line that creates an instance specifies the type for the new variable (Rectangle) followed by the variable name that will hold the reference to the new instance (rectangle1, rectangle2, rectangle3, or rectangle4). Then each line assigns the result of using the new keyword followed by the desired value for the width and height arguments separated by a comma and enclosed in parentheses.

Tip

In Java 9, we have to specify the type for the variable in which we want to hold the reference to an instance. In this case, we declare each variable with the Rectangle type. In case you have experience with other programming languages that provide a keyword to generate implicitly typed local variables such as the var keyword in C#, you must know there is no equivalent in Java 9.

After we enter all the lines that declare the class and create the four instances in JShell, we will see four messages that say "Initializing a new Rectangle instance" followed by the width and height values specified in the call to the constructor of each instance. The following screenshot shows the results of executing the code in JShell:

After we execute the previous lines, we can check the values for the width and height fields for each of the instances we have created. The following lines show expressions that JShell can evaluate to display the values for each field. The code file for the sample is included in the java_9_oop_chapter_03_01 folder, in the example03_05.java file.

rectangle1.width
rectangle1.height
rectangle2.width
rectangle2.height
rectangle3.width
rectangle3.height
rectangle4.width
rectangle4.height

The following screenshot shows the results of evaluating the previous expressions in JShell.

Enter the following expression in JShell. The code file for the sample is included in the java_9_oop_chapter_03_01 folder, in the example03_06.java file.

rectangle1 instanceof Rectangle

JShell will display true as a result of the evaluation of the previous expression because rectangle1 is an instance of the Rectangle class. The instanceof keyword allows us to test whether an object is of the specified type. With this keyword, we can determine whether an object is a Rectangle object.

As previously explained, Rectangle is a subclass of the java.lang.Object class. JShell already imported all the types from java.lang, and therefore, we can just reference this class as Object. Enter the following expression in JShell. The code file for the sample is included in the java_9_oop_chapter_03_01 folder, in the example03_07.java file.

rectangle1 instanceof Object

JShell will display true as a result of the evaluation of the previous expression because rectangle1 is also an instance of the java.lang.Object class.

Enter the following expression in JShell. The code file for the sample is included in the java_9_oop_chapter_03_01 folder, in the example03_08.java file.

rectangle1.getClass().getName()

JShell will display "Rectangle" as a result for the previous line because the rectangle1 variable holds an instance of the Rectangle class. The getClass method allows us to retrieve the runtime class of an object. The method is inherited from the java.lang.Object class. The getName method converts the runtime type to a string.

Now, we will try to create an instance of Rectangle without providing arguments. The following line won't allow Java to compile the code and will display a build error in JShell because the compiler cannot find a parameterless constructor declared in the Rectangle class. The only constructor declared for this class requires two double arguments, and therefore, Java doesn't allow Rectangle instances to be created without specifying the values for width and height. The code file for the sample is included in the java_9_oop_chapter_03_01 folder, in the example03_09.java file.

Rectangle rectangleError = new Rectangle();

The next screenshot shows the detailed error message: