Methods: Communicating with Objects

We communicate with objects using methods. Methods are executable code within each object, for which an interface has been established. Sometimes the interface is only for the object itself. Other times it is an interface accessible by other objects. This chapter discusses that topic in detail.

3.3 Constructor Methods

In the previous section, we looked at several examples of mutator methods that change the values of private instance variables of an object. It is possible to define mutator methods to set the initial values of instance variables after an object is created, but initial values can also be set by constructors.

As you recall from Chapter 0, a constructor method is used to create Constructor names an instance (or object) of a class and to assign initial values to instance variables. A constructor declaration looks just like a method definition except it must have the same name as the class, and it cannot declare a result type. Unlike the class level variables and methods of a class, constructors are not considered members of the class. Therefore, they are not inherited by a class’s subclasses. Access to constructors is governed by the access modifiers public and private. Here is a simple constructor for our OneRowNim class:

Annotation 2020-03-23 213628

This constructor merely sets the initial values of the instance variables, nSticks and player. In our current version of OneRowNim these variables are given initial values by using initializer statements when they are first declared:

Annotation 2020-03-23 213900

So we now have two ways to initialize a class’s instance variables. In the OneRowNim class it doesn’t really matter which way we do it. However, the constructor provides more flexibility because it allows the state of the object to be initialized at runtime. Of course, it would be somewhat redundant (though permissible) to initialize the same variable twice, once when it is declared and again in the constructor, so we should choose one or the other way to do this. For now, let’s stick with initializing the instance variables when they are declared.

Annotation 2020-03-23 214006

A constructor cannot return a value and, therefore, its declaration cannot include a return type. Because they cannot return values, constructors cannot be invoked by a regular method invocation. Instead, constructors are invoked as part of an instance creation expression when instance objects are created. An instance creation expression involves the keyword new followed by the constructor invocation:

Annotation 2020-03-23 214136

Note here that we have combined variable declaration and instantiation into a single statement, whereas in some previous examples we used separate declaration and instantiation statements. Either way is acceptable.

Annotation 2020-03-23 214315

Constructors should be used to perform the necessary initialization operations during object creation. In the case of a OneRowNim object, what initializations could be performed? One initialization that would seem State initialization appropriate is to initialize the initial number of sticks to a number specified. In order to do this, we would need a constructor with a single int parameter:

Annotation 2020-03-23 214416

Now that we have this constructor we can use it when we create instances of OneRowNim:

Annotation 2020-03-23 214511

The effect of these statements is the same as if we had used the setSticks() method that was discussed briefly on page 103. The difference is that we can now set the number of sticks when we create the object.

Should we keep the preceding constructor, or keep the setSticks() method or keep both in our class definition? The constructor can only be invoked as part of a new statement when the object is created but the setSticks() method could be called anytime we want. In many cases, having redundant methods for doing the same task in different ways would be an asset, because it allows for more flexibility in how the class could be used. However, for a game like One Row Nim, a major concern is that the two instance variables get changed only in a manner consistent with the rules for One Row Nim. The best way to guarantee this is to have takeSticks() as the only method that changes the instance variables nSticks and player. The only time that it should be possible to set the number of sticks for a game is when a constructor is used to create a new instance of OneRowNim.


SELF-STUDY EXERCISES

EXERCISE 3.5 What’s wrong with the following constructor definition?

Annotation 2020-03-23 214826

EXERCISE 3.6 Change the OneRowNim(int sticks) constructor so that it sets the number of sticks and also have it also set player two as the player who takes the first turn.