Wednesday, September 25th - 810:061

  1. Announcements
  2. "this"
  3. Parameters
  4. Instance variables
  5. , aka PIV (Private Instance Variables = PIV). Remember, ALL instance variables are PRIVATE.
  6. Instance variables and parameters
  7. Local variables
  8. Local variables vs instance variables vs parameters


Announcements

Next programming assignment will be handed out on Friday

 


"this"

The keyword "this" is Java's way of letting an object refer to itself.

Let's explore this a bit more by looking again at the Tshirt class that we've been studying:

import objectdraw.*;
import java.awt.*;

// A class that defines a graphical type that looks a bit
// like a t-shirt
public class Tshirt {

        // overall size of shirt
    private static final double SIZE = 120;
    
        // size of sleeves
    private static final double SLEEVE_WIDTH = SIZE;        
    private static final double SLEEVE_HEIGHT = 0.2*SIZE;

        // size of body
    private static final double BODY_WIDTH = 0.6*SIZE;
    private static final double BODY_HEIGHT = (0.65)*SIZE;
    
        // horizontal inset of body from sleeve
    private static final double BODY_INSET = 0.2*SIZE;
    
        // size of neck
    private static final double NECK_WIDTH = 0.3*SIZE;
    private static final double NECK_HEIGHT = 0.06*SIZE;
    
        // horizontal inset of neck from sleeve
    private static final double NECK_INSET = 0.35*SIZE;


    private FramedRect sleeveTrim, bodyTrim;    // rectangles that form a 
    private FramedOval neckTrim;                // border around the shirt

    private FilledRect body, sleeves;           // rectangles that form the 
    private FilledOval neck;                    // interior color of the shirt


    // create a new Tshirt with its upper left corner at (x,y)
    public Tshirt( double x, double y, DrawingCanvas canvas )
    {
            // create boundary rectangles
        sleeveTrim = new FramedRect( x, y + NECK_HEIGHT/2, SLEEVE_WIDTH, 
                                         SLEEVE_HEIGHT, canvas);
        bodyTrim = new FramedRect( x + BODY_INSET, y + NECK_HEIGHT/2, 
                                         BODY_WIDTH, BODY_HEIGHT, canvas);
        
            // create interior rectangles
        sleeves = new FilledRect( x+1, y+NECK_HEIGHT/2+1, SLEEVE_WIDTH-1, 
                                      SLEEVE_HEIGHT-1, canvas);
        body = new FilledRect( x+BODY_INSET+1, y+NECK_HEIGHT/2+1, 
                                   BODY_WIDTH-1, BODY_HEIGHT-1, canvas);

            // give it a neck hole
        neck = new FilledOval( x + NECK_INSET, y, NECK_WIDTH, 
                                   NECK_HEIGHT, canvas);
        neckTrim = new FramedOval( x + NECK_INSET, y, NECK_WIDTH, 
                                       NECK_HEIGHT, canvas);
        
            // set the interior to white
        body.setColor(Color.white);
        neck.setColor(Color.white);
        sleeves.setColor(Color.white);
    }

    // move the t-shirt by specified offsets.
    public void move( double xOffset, double yOffset )
    {
        body.move(xOffset,yOffset);
        neck.move(xOffset,yOffset);
        sleeves.move(xOffset,yOffset);
        bodyTrim.move(xOffset,yOffset);
        sleeveTrim.move(xOffset,yOffset);
        neckTrim.move(xOffset,yOffset);
    }

    // move the t-shirt to a new upper-left coordinate position
    public void moveTo( double x, double y)
    {
        move( x - sleeves.getX(), y - neck.getY());
    }

    // returns true if the t-shirt contains the point; false otherwise
    public boolean contains( Location pt)
    {
        return body.contains(pt) || sleeves.contains(pt) || 
               neck.contains(pt);
    }
}

As we discussed earlier, the method move is straightforward. It simply moves all 6 pieces of the t-shirt the same amount. The method moveTo uses a clever trick. Rather than computing where to move each of the six pieces (notice that all 6 of them are created at different positions in the constructor), the method calculates how far (in each of the x and y directions) the new position is from the left and top of the entire t-shirt.

Notice the left edge of the t-shirt is that of the sleeves, while the top is the same as that of the neck. Hence the distance to move the t-shirt in the x direction is x - sleeves.getX(), while the distance to move the T-shirt in the y-direction is given by y - neck.getY(). Now the move method is invoked with those computed values as the distance to move in the x and y directions.

We took advantage of the fact that we had already written the move method, and used it to perform the actions necessary for moveTo. We had to compute how far to move it from the absolute coordinates given by the parameters of moveTo, but this was significantly easier than calculating where to move each individual piece.

There is one strange thing about the body of the moveTo method:

    move( x - sleeves.getX(), y - neck.getY());

The move message is not sent to an object! We are used to writing body.move(...), neck.move(...), etc., but not just writing move by itself without an object that it is being sent to.

When we write a method name by itself inside the body of another method, it means execute that method in the same class. Technically, Java considers the line of code above as an abbreviation for:

    this.move( x - sleeves.getX(), y - neck.getY());

The new version looks a bit more familiar, in that at least now the move message is being sent to an object, this. As we described above, this stands for the object executing the current method.


Parameters

Parameters are used to pass information from one part of a program to another. Let's look back at the general shape of the t-shirt class (where we have omitted all constants, comments, and details of method bodies so that we can focus on parameters and instance variables):

public class Tshirt{

    private FramedRect sleeveTrim, bodyTrim;    
    private FramedOval neckTrim;                

    private FilledRect body, sleeves;            
    private FilledOval neck;            
            
    public Tshirt( double x, double y, DrawingCanvas canvas ){...}

    public void move( double xOffset, double yOffset ){...}

    public void moveTo( double x, double y){...}

    public boolean contains( Location pt){...}

    public void setColor(Color newColor){...}
        
}

The Tshirt constructor has formal parameters x, y, and canvas. Just like the instance variables, the parameters must be supplied with a type definition so that we know what kind of values they can hold. Inside the constructor we can use any of the instance variables (and constants) and the three formal parameters declared in the constructor header.

Look at the code in the constructor body:

public Tshirt( double x, double y, DrawingCanvas canvas )
{
        // create boundary rectangles
    sleeveTrim = new FramedRect( x, y + NECK_HEIGHT/2, SLEEVE_WIDTH, 
                                 SLEEVE_HEIGHT, canvas);
    bodyTrim = new FramedRect( x + BODY_INSET, y + NECK_HEIGHT/2, 
                               BODY_WIDTH, BODY_HEIGHT, canvas);
    
        // create interior rectangles
    sleeves = new FilledRect( x+1, y+NECK_HEIGHT/2+1, SLEEVE_WIDTH-1, 
                              SLEEVE_HEIGHT-1, canvas);
    body = new FilledRect( x+BODY_INSET+1, y+NECK_HEIGHT/2+1, 
                           BODY_WIDTH-1, BODY_HEIGHT-1, canvas);

        // give it a neck hole
    neck = new FilledOval( x + NECK_INSET, y, NECK_WIDTH, 
                           NECK_HEIGHT, canvas);
    neckTrim = new FramedOval( x + NECK_INSET, y, NECK_WIDTH, 
                               NECK_HEIGHT, canvas);
    
        // set the interior to white
    body.setColor(Color.white);
    neck.setColor(Color.white);
    sleeves.setColor(Color.white);
}

Notice that all of the parameters x, y, and canvas are used in the constructor. If any were not used, we would have dropped them from the list of parameters.

The parameters declared in the headers of methods and constructors are called formal parameters because they are place holders for the actual values (i.e., actual parameters) which will be passed in at run-time. In the Tshirt constructor above, x, y, and canvas are formal parameters. To actually construct a Tshirt object, we write something like

   new Tshirt(100,150,canvas)

In this call the actual parameters are 100, 150, and canvas. In such a call, Java matches up actual and formal parameters by position. Thus 100 is matched up with the formal parameter x, 150 is matched up with y, and the actual parameter canvas (e.g., from Drag2Shirt) is matched up with the formal parameter canvas (the parameter in the Tshirt class).

For the last of these, the name of the actual parameter and formal parameter are the same, but this is a coincidence rather than being significant. The fact that the names are the same is irrelevant, because they are names of distinct variables declared in different classes. It is no more significant than the fact that there are two students named Smith in the class.

Because we will try to use names that are descriptive, we will often end up with these accidental correspondences in names of actual and formal parameters. If you find that confusing, you can always use different names. For example, we could uniformly change each occurrence of the name of the last formal parameter in the Tshirt constructor to aCanvas with no impact on the execution of the program.


Instance variables

Instance variables are used to remember information that is needed between invocations of methods in a class. A good example is the instance variable lastPoint that we have declared in the various dragging programs we have written. lastPoint is initialized each time the onMousePress method is executed. It is used and updated each time onMouseDrag is executed. If lastPoint was not an instance variable, it would not still be around after execution of each of these methods, and hence would not be available when it was needed in the next invocation of onMouseDrag.

Instance variables are usually initialized either in their declarations or in the constructor, as they were in our Tshirt class. Occasionally there will be an instance variable which is not initialized in either of those places. lastPoint is a good example of such an instance variable.


Instance variables and parameters

Let's now review one of the exercises we did earlier -- the one in which we added a reset method to the Tshirt class. This method moves the t-shirt back to its original starting position. The body simply invokes the moveTo method on the t-shirt, but we need to know where to move it back to. The x and y values passed in as parameters to the constructor are the key to that information. But because they're parameters, they're forgotten as soon as the constructor is done executing.

To remember these values, we must save them as instance variables. That is, we need to introduce two new instance variables, say startX and startY. They will be used to save the values of the constructor parameters x and y so that they can be used in the method reset. Because x and y are declared to be of type double in the constructor declaration, startX and startY must be declared to be of the same type. At the end of the Tshirt constructor we add two lines initializing the values of startX and startY. Because these variables are instance variables, they will still be around when we execute the reset method.

The skeleton of this updated class is shown below:

public class Tshirt{
    ...           
    private double startX, startY;            
            
    public Tshirt( double x, double y, DrawingCanvas canvas )
    {
       ...
       startX = x;
       startY = y;
    }

    ...
    
    public void moveTo( double x, double y){...}

    public void reset()
    {
       this.moveTo(startX, startY);
    }       
}

The use of instance variables to remember the values of parameters to constructors is very common, and will occur in many of your labs. For example, the Pole class, which is provided for you in the Magnet lab, contains an instance variable which is used to remember the object of type Magnet which was passed in to the constructor.


Local variables

Local variables differ from instance variables in that they are needed only during the execution of a single method and need not be remembered after that method has completed execution. A good example of a variable that should be declared local is the variables used to generate a new laundry-item color in the Laundry lab. Recall that on the release of the mouse, we had to check whether the user had dropped the clothing into the correct laundry bin. If so, we generated a random integer between 1 and 3. The segment of code that does the new color generation (and determination of the target bin) is as follows:

    public void onMouseRelease(Location point) {
        int colorNumber;

	if (dragging && correctBasket.contains(point)) {	
	    colorNumber = generator.nextValue();

                 ...


    } // end onMouseRelease

The variable colorNumber is not declared as a parameter of onMouseRelease. However, it is not necessary to declare it as an instance variable, as there is no need to remember it between invocations of method onMouseRelease. Its value is calculated from scratch each time the method is executed, and hence there is no reason to remember the old value.

As a result, colorNumber should be declared as a local variable of the method onMouseRelease. Local variables are only visible inside the method they are declared in, and their values disappear once the current execution of the method ends. In this way they are similar to parameters, but unlike parameters they are not provided with values by actual parameters sent in from the outside. They must be initialized inside the method body.

Notice that local variable declarations are not labelled as private since they are automatically even more restricted than private instance variables. They can only be seen and used within the body of the method they are declared in, and go out of existence when the method terminates.

In the Laundry lab, some of you made the first swatch of cloth white by default. Others made it a random color, as above. If you did the latter, then your begin method would contain the code:

    public void begin() {
        int colorNumber = generator.nextValue();

        if (colorNumber == 1) {
            laundryItem.setColor(Color.white);
            correctBasket = whites;
        } else if (colorNumber == 2) {

                 ...

    } // end begin

In this case, is it still correct to make colorNumber a local variable? It is used in more than one method, so shouldn't it be an instance variable? The answers to these to questions are yes and no. It is correct to declare them as local variables. There are a couple of reasons for this:

  1. the values of these variables in begin are not needed in onMouseRelease. They are completely independent of each other.
  2. the values of these variables don't need to be remembered from one invocation of onMouseRelease to the next. The necessary values are generated anew.


Local variables vs instance variables vs parameters

From now on, be careful of where you declare variables.