behind the scenes
last | | contents | | next


The thing germinated and bred itself. It proceeded according to its own logic. What did I do? I followed the indications. I kept a sharp eye on the clues I found myself dropping. The writing arranged itself with no trouble into dramatic terms. The characters sounded in my ears---it was apparent to me what one would say and what would be the other's response, at any given point. It was apparent to me what they would not, ever, say, whatever one might wish... When the thing was well cooked I began to form certain conclusions. The point is, however, that by that time the play was now its own world. It was determined by its own engendering image.
Harold Pinter, on The Birthday Party

 

While we've now learned a lot about the Java interpreter's capabilities, SimpleRole is more of a germ of an idea, rather than any kind of real play. An actor comes onstage, divides two numbers, reports the result, then goes offstage again. It's the epitome of boredom, and it will stay that way for quite some time as we add to it to better understand how the Java interpreter does its job.


 
Specifying a Variable's Scope

As we've seen, we can declare variables inside methods just as we can declare them inside classes. For example, SimpleRole's first variable, numberOfTurnips, appears at the beginning of SimpleRole, but the numberOfPeople variable first appears at the beginning of divideTurnipsAmongPeople(). No matter where we declare a variable though (that is, no matter where we ask the stage manager to produce a variable of that name and type) we can't use it until that point.

If we declare a variable inside a method we can only use it inside the method. The method is the variable's scope. The method divideTurnipsAmongPeople(), for instance, is numberOfPeople's scope. The scope of the other variables (numberOfTurnips, turnipsPerPerson, and finishedDividing), which we declared outside divideTurnipsAmongPeople() but inside the class, is the entire class, namely, SimpleRole. Thus, since we declare the reference variable estragon in SimpleRole's main() method we can't use it in, say, divideTurnipsAmongPeople(). Its scope is main(), so that's the only method we can use it in.

In theater terms, each method in a class is like a scene in a play. In that scene an actor may have special props (like numberOfPeople) as well as its usual props; outside that scene it only has its usual props. The props we define inside a scene are local to that particular scene; those we define inside the role as a whole are global to all the role's scenes. A method's local props appear when an actor starts executing the method and vanish when the actor finishes executing that method. An actor's global props exist for as long as the actor exists.

Let's say we're writing a role called WhaleHunter. An actor playing this role, let's call it Ishmael, will always have its own copy of all the global props---say, a sword, a snuff-box, and a wallet. Our prop declarations ask the stage manager to give Ishmael, along with every other actor playing the role, its own copy of each of those props. Within a particular method, though, Ishmael is acting specially (perhaps it is method acting?) and so may need special props---say, a boat, a white whale, and a harpoon. Ishmael will always have its (global) sword, snuff-box, and wallet, but for the duration of its time in the particular method it will also have a (local) boat, white whale, and harpoon. Those local props vanish once the actor exits the method.


 
Parameterizing Methods

If we want to change the number of people we're dividing turnips among in SimpleRole we'd have to go in and change the value 2 in the assignment statement:


   numberOfPeople = 2;

Instead, we could pass in numberOfPeople to divideTurnipsAmongPeople() as a parameter---something that can vary in value while the method itself remains the same. After all, within the method it doesn't much matter what the number of people are, as long as it's an int and is one or more.

The method declaration would then become:


   double divideTurnipsAmongPeople(int numberOfPeople)

This has the same effect as declaring numberOfPeople inside the method as we've been doing so far. The variable numberOfPeople is still an int and it is still local to the method divideTurnipsAmongPeople() (that is, it only exists within the method).

Parameterizing the method, however, makes it more useful since the number of people is no longer fixed at two. If we wanted our lone SimpleRole character, estragon, to divide turnips among 15 people instead of two, say, we would send it the message:


   estragon.divideTurnipsAmongPeople(15);

Since we declared numberOfPeople as an int in the method declaration we can't ask estragon to execute its method with a non-int value like 52.34 or true. The type of the value we send and the type of the variable we declare as a parameter must be the same. The Java interpreter will check.
 
Adding Multiple Parameters

We can declare any method (except the special version of main()) as taking any number and type of parameters. In particular, we should give SimpleRole's method at least one more parameter---the number of turnips. That would make it yet more general because then we wouldn't have to go in all the time and make changes to the method directly.

We tell the Java interpreter that a method takes multiple parameters by listing the parameters separated by commas in the method declaration. As always, we must still declare the name and type of the parameters.

The method would then become:


   double divideTurnipsAmongPeople(int numberOfTurnips,
      int numberOfPeople)
      {
      turnipPortions = numberOfTurnips / numberOfPeople;
      finishedDividing = true;

      return turnipPortions;
      }

Here the declaration of divideTurnipsAmongPeople() spreads over two lines to keep each line's width down. The Java interpreter doesn't care about lines---it cares about statements. We can continue any statement across any number of lines we wish, so we could just as easily say:


   double
   divideTurnipsAmongPeople(
   int numberOfTurnips,
   int numberOfPeople)

and it would have exactly the same effect as far as the Java interpreter is concerned. Only the human beings reading our code would hate us.

With the new parameter, we could ask estragon to divide six turnips among two people from within the main() method, by sending the message:


   estragon.divideTurnipsAmongPeople(6, 2);

By freeing the method to handle any pair of int values we can now have any actor ask any SimpleRole actor to execute the method with any pair of int values as parameters. The method is no longer useful only to the class we originally wrote it for.


 
Returning from Methods

Just as with the original divideTurnipsAmongPeople() method that accepted no parameters, methods may also return no values. We tell the stage manager that with the word void. An actor executing a void method will never return any values. It will still return of course, but the method it's executing will have no statement like:


   return [variable name];

We can still force an actor executing a void method to return (but with no returned value) by issuing a bare


   return;

statement, with no variable name attached.

Also, since a void method never returns a value, an actor executing it can return for itself once it runs out of statements to execute. This can't happen in a non-void method since we must always tell the stage manager which value the actor must return. So we must always specify a return explicitly once the method isn't void.

The main() method, as now written, is an example of a method that an object exits when there are no more statements for the object to execute rather than when we explicitly ask it to.

This clears up a little of the mystery of the declaration:


   public static void main(String[] parameters)

This statement declares a method named main() that accepts one parameter and returns no values at all. Its sole parameter is passed in to the method from somewhere---from where exactly is still a mystery---and the parameter's type, String[], is also still a mystery.
 
Adding Conditional Behavior

Now that we've parameterized divideTurnipsAmongPeople() we've laid ourselves open to a problem we couldn't have had before: namely, some actor might misuse the method by asking a SimpleRole actor to try to divide some turnips by zero or fewer people. We need some way for actors playing SimpleRole to test the value of numberOfPeople and avoid the division if that value is zero or less. If it's zero or less, the actor should refuse to do anything at all and simply return with a value of zero for the method. We can do that with an if statement, as follows:


   double divideTurnipsAmongPeople(int numberOfTurnips,
      int numberOfPeople)
      {
      finishedDividing = false;

      if (numberOfPeople <= 0)
         return 0;

      turnipPortions = numberOfTurnips / numberOfPeople;
      finishedDividing = true;

      return turnipPortions;
      }

The symbol sequence "<=" is the "less than or equal to" operator, and the statement:


   if (numberOfPeople <= 0)

asks whether the value currently in the variable numberOfPeople is zero or less. If that's true, then the SimpleRole actor executes the return statement the if statement encloses, and, thus returns from the method right away. If, on the other hand, numberOfPeople's value is one or more, then the actor jumps past the return statement, executes the next statement in sequence (the division), and continues on from there as before.

Consequently, whenever the value of numberOfPeople is less than one, estragon does nothing when asked to divideTurnipsAmongPeople(), simply returning zero. Whenever its value is one or more, though, estragon obligingly does the division and returns the result.

Because we declare the method as returning a double, every time we ask an actor executing the method to return from the method, we must also specify which double value to return. An actor could never simply return from a method that has a declared return type; it can only do so if the method is void.


 
Printing Output

We're still not quite done. We haven't yet asked anyone, neither the stage manager nor estragon, to tell us the result of the division. So let's modify the main() method to ask the stage manager to print something to the screen:


   public static void main(String[] parameters)
      {
      SimpleRole estragon;

      estragon = new SimpleRole();

      double portionsPerPerson;

      portionsPerPerson = estragon.divideTurnipsAmongPeople(6, 2);

      System.out.println("Number of portions per person is: " +
         portionsPerPerson);
      }
   }

Without going into exactly how the last statement works, it's enough to know for now that it asks the stage manager to print the number of portions per person. Also, this statement covers two lines, just as the declaration of divideTurnipsAmongPeople(). We can stretch any statement across as many lines as we wish. It's wise, however, to indent any continuation lines to make it clear that they're all part of one statement.


 
Accessing Variables

Just as we can send messages to objects asking them to execute their methods, we can also send messages asking them to access their variables. For example, the message:


   estragon.finishedDividing;

asks estragon to let us access its copy of its finishedDividing variable. We can use this capability to improve our program's output.

Recall that if the value of numberOfPeople were zero or less, then after requesting estragon to execute its method, the computation would have been unsuccessful and estragon's copy of finishedDividing would have the value false. If, however, the computation were successful, it would have the value true.

Thus we can tell whether our request to estragon to divide some turnips among some people completed successfully or not by testing estragon's copy of the finishedDividing variable as follows:


   public static void main(String[] parameters)
      {
      SimpleRole estragon;

      estragon = new SimpleRole();

      double portionsPerPerson;

      portionsPerPerson = estragon.divideTurnipsAmongPeople(6, 2);

      if (estragon.finishedDividing == false)
         return;

      System.out.println("Number of portions per person is: " +
         portionsPerPerson);
      }
   }

Here we're asking the stage manager to ask estragon to divide six turnips among two people. Then we test the value of estragon's finishedDividing variable (which is a boolean) to see if it's equal to the boolean value false using the equals operator, the symbol sequence "==". If it's false then estragon's computation failed, otherwise the computation succeeded. (Note that the equals operator, like every other operator we've seen, is just like a tiny method; this one takes two variables as parameters and returns true if their values match.)

If estragon's computation were unsuccessful, the stage manager immediately returns from main() (which ends the program). Otherwise the stage manager continues and prints the value of the division, then runs out of statements in main() to execute (which also ends the program). Since main()'s return type is void, we don't return a value from it, so we can end it simply by running out of statements to execute.


 
Adding Comments

It's time to stop and take stock. The Java interpreter ignores everything following the symbol sequence "//" until the end of the line, and it ignores everything, including new lines, between the symbol sequences "/*" and "*/". So we can use those symbol sequences to comment to ourselves to help us remember something, or to explain to other programmers what we're doing. Here then is the entire role again, in slow motion replay with copious comments on each statement to help explain them. (Note: the following program is seriously overcommented; ideally, comments should be rare since the code should speak for itself, if well-written.)


/*
First, we tell the Java interpreter
that we're about to define a new class of objects.
*/

class SimpleRole
   {
   /*
   Next we declare the names and types of some variables
   that all such objects will have their own copies of.

   All three subsequent declarations end with semicolons.
   That's a Java rule: all statements must end in a semicolon;
   except for braces, brackets, and parentheses since they
   always come in matched pairs, so the Java interpreter can
   figure out where they end for itself.
   */

   int numberOfTurnips;
   double turnipPortions;
   boolean finishedDividing;

   //We can separate the declaration statements above
   //from the method definitions below with any number
   //of empty lines. We can also add as many comment lines
   //as we want. The Java interpreter will ignore them all.

   //Next comes the definition of the method
   //divideTurnipsAmongPeople(). To keep the line width down,
   //its declaration spans two lines.

   double divideTurnipsAmongPeople(int numberOfTurnips,
      int numberOfPeople)
      {
      /*
      Method definitions go inside matching braces,
      just like class definitions.

      The variables numberOfTurnips and numberOfPeople are
      parameters of this method; they are local to the method
      so we cannot use them outside it. We can, however, use
      the three global ones, since they're declared in the class
      and not any particular method, they're available to all
      methods.

      These comments are indented along with the method
      definition they comment on. Everything inside a definition
      should have the same indentation. This isn't a Java rule
      but it's good style because it makes it easier to see
      the overall structure.
      */

      finishedDividing = false;

      //If the value of numberOfPeople is impossible,
      //simply return a value of 0 without doing anything.

      if (numberOfPeople <= 0)
         return 0; //return 0 to whoever asked
                   //an actor to execute this method.

      //If we get to this point, the value of numberOfpeople
      //must be one or more, so do the division.

      turnipPortions = numberOfTurnips / numberOfPeople;
      finishedDividing = true;

      //We should separate the following line
      //from the above lines because it's fundamentally
      //different. No assignment or computation is taking
      //place below, it's simply returning a value.
      //Again, this isn't a Java rule, it's style.

      return turnipPortions; //Return the value in turnipPortions
                            //to whoever asked a SimpleRole actor
                            //to execute this method.
      }

   //Next comes the definition of the mysterious main() method.

   public static void main(String[] parameters)
      {
      //Create a local reference variable and name it estragon.

      SimpleRole estragon;

      //Create a SimpleRole actor and stick its (real) name
      //in variable estragon.

      estragon = new SimpleRole();

      //Reference variable estragon now contains a reference
      //value that points to the newly created actor
      //(whose real name we don't know).
      //Now we can send that actor messages using ``estragon''
      //as its character's name.

      //Create a local double variable
      //and name it portionsPerPerson.

      double portionsPerPerson;

      //Send a message to estragon asking it to execute its
      //divideTurnipsAmongPeople() method to divide 6 turnips
      //among 2 people and put the double result estragon returns
      //into portionsPerPerson.

      portionsPerPerson = estragon.divideTurnipsAmongPeople(6, 2);

      //If the computation was unsuccessful
      //(that is, if estragon's copy of finishedDividing
      //is false), simply end this method.

      if (estragon.finishedDividing == false)
         return;

      //estragon's computation must have completed successfully,
      //otherwise this main() method would have already
      //returned, so print the value of portionsPerPerson.

      //The following (magic) statement spans two lines
      //to keep the overall line width reasonable.

      System.out.println("Number of portions per person is: " +
         portionsPerPerson);

      //End the method by running out of statements to execute.
      }
   }


last | | contents | | next