APE01 - Method in your Madness

From Monkeys @ Keyboards
Jump to: navigation, search

Method in your Madness

APE Antics - Michael James Heron and Pauline Belford


Previous TOC Next
APE01 - Case Study 4 - A Library of Functionality APE Antics APE01 - An Object Lesson 1 - Objects and Classes (Advanced)

Introduction

APE gives you a set of methods for making pacman do things, but that set of methods is (by design) somewhat limited. In the past few chapters we've learned about all sorts of excellent things, but we're still stuck with the same fairly limited commands that we can issue - he can move, he can turn, and he can sense obstacles in front of him. What else can he do? Well, not a lot.

However, through the application of Cunning Sections Of Code, we can make him do much more - we can make him walk until he sees an obstacle, we can make him turn around until a certain kind of element is in front of him - our power over Pacman is quite considerable, but every time we want him to do any of these complicated things we need to write the code again and again.

This causes problems, and in the course of this chapter we'll look at how we can fix these problems through a new kind of programming structure called the method.

The Method

A method is a self-contained piece of code that sits, dormant, until you call it to action through the rhythmic chanting of occult and eldritch runes. In Java, that chanting has a particular form, and that form dictates what method we are going to call, and what information we are going to provide to that method to allow it to do its dark magic.

The easiest way to think of a method is that it is a completely self-contained program. It has no relationship to your main method, and the code in the method will never be executed without explicit instructions. We will see later in this chapter that we can actually setup a relationship between methods, but for now we'll think of them as completely independent.

We've already seen the main method - this is a special method that is understood by Java. We can add our own methods into a program, but Java has no idea what they are and what they are supposed to do. We need to provide it with that information.

The method you write does not know anything at all about what's going on in your program. It doesn't know where pacman is. It doesn't know *what* Pacman is. It doesn't know any of the variables you have declared, and it would only look confused if you tried to reference them. In short, it is helpless and bereft of information about the world in which it exists.

Let's look at the simplest kind of method - one that just displays a piece of text to the console window:

<source lang = "java"> public void printMessageToConsole() {

 System.out.println ("AAAaaAAaaAAa!")  

} </source>

We can incorporate this method into our own programs - it comes *after* the closing brace of the main method, but before the closing brace of the class itself:

<source lang = "java">

import draconia.APE.pacman.*;

public class MethodMonkey {

   public static void main(String args[]) {
       PacmanGame main = new PacmanGame();
       main.setMap("blank");
       main.showGame();
   }
   public static void printMessageToConsole() {
       System.out.println ("AAAaaAAaaAAa!")  
   }

} </source>


This is important - if you put the method inside your main method, Java will complain. If you put it outside of the class, it will complain. The method belongs to a particular class, and so your ownership with the braces should reflect that. So, no:

<source lang = "java">

 import draconia.APE.pacman.* ;
 public class MethodMonkey {
   public static void main  ( String args[] )   {
     PacmanGame main = new PacmanGame ();
     main.setMap("blank");
     main.showGame();

// NO. BAD DOG.

     public static void printMessageToConsole  (  )    {
     }
  }

} </source>


Attempting to do this will give you the following arcane error message:

F:\Projects\APE Tool\Teaching Material\chapter 08\MethodMonkey\MethodMonkey.java:10:
illegal start of expression
void printMessageToConsole() {


And no:

<source lang = "java">

import draconia.APE.pacman.*;

public class MethodMonkey {

   public static void main(String args[]) {
       PacmanGame main = new PacmanGame();
       main.setMap("blank");
       main.showGame();
   }

} // NO. BAD DOG. public static void printMessageToConsole () { }

</source>


Attempting to do this will give you the following equally arcane error message:


F:\Projects\APE Tool\Teaching Material\chapter 08\MethodMonkey\MethodMonkey.java:14:
'class' or 'interface' expected
void printMessageToConsole() {

If you see these error messages, you have your method in the wrong place and should move it to where it should be - after the closing brace of the main method, and before the closing brace of your class.

You can add as many methods as you like into your program, and they are all located in the same way - outside of the braces of any of your methods, and before the closing brace of your class:

<source lang = "java">

import draconia.APE.pacman.*;

public class MethodMonkey {

   public static void main(String args[]) {
       PacmanGame main = new PacmanGame();
       main.setMap("blank");
       main.showGame();
   }
   public static void printMessageToConsole() {
       System.out.println("AaAaAAAaAAaaa?");
   }
   public static void printADifferentMessageToConsole() {
       System.out.println("There are zombies *everywhere*!");
   }

}

</source>

Make sure you follow this, and you'll be fine.

So, compile and run the program. Aaaaaand - nothing happens. Pacman sits there with a stupid look on his face, and doesn't do anything else. Where's our message? It's nowhere to be found. Something is wrong in the state of Gotham!

This is because your method exists independently of the rest of the program. You need to bring it to life with the aforementioned arcane chanting - in programming terminology, we call this an invocation.


Terminology Alert
Invocation
An invocation is the process of making a method run the code it contains. More colloquially, it is known as 'calling a method'.

So we invoke the method, or we call the method - and the magic spell used to do this is just the name of the method with the brackets:

<source lang = "java"> printMessageToConsole (); </source>

For example:

<source lang = "java">

import draconia.APE.pacman.*;

public class MethodMonkey {

   public static void main(String args[]) {
       PacmanGame main = new PacmanGame();
       main.setMap("blank");
       main.showGame();
       
       printMessageToConsole();
   }
   public static void printMessageToConsole() {
       System.out.println("AaAaAAAaAAaaa?");
   }
   public static void printADifferentMessageToConsole() {
       System.out.println("There are zombies *everywhere*!");
   }

} </source>

Now we compile and run, and we get the following output in our console window:

13.1: Console Output

This is exactly what we're looking for!

But why are we printing the message to the console when we have a perfectly good pacman game there? Let's change the method a little:

<source lang = "java">

   public static void printMessageToConsole() {
       main.sendMessage ("AaAaAAAaAAaaa?");
   }

</source>

What happens when we compile it now? An error message:

F:\Projects\APE Tool\Teaching Material\chapter 08\MethodMonkey\MethodMonkey.java:14: cannot find symbol
symbol  : variable main
location: class MethodMonkey
main.sendMessage ("AaAaAAAaAAaaa?");

This is again because of the rule indicated above - our method knows nothing about the world it around it. The main method knows what the pacman game is. The printMessageToConsole method doesn't. If we want our method to be able to do anything with the pacman game, we need to tell it about the game.

The Method Exploded

Before we talk about how we do that, let's look at the method itself - it's almost identical to the main method in structure, except for the name and the brackets:

<source lang = "java"> public static void main (String args[]) { </source>

<source lang = "java"> public static void printMessageToConsole () { </source>

We're not going to trouble ourselves about the public or the static - they are there because they have to be. If you are really concerned about what they are for, then check out the Javanomicon - but don't say I didn't warn you.

The rest of the method describes three specific sets of information: the name of the method, the return type, and the parameters. Let's explode that up and have a look (ignoring the bits we don't care about here):

13.2: The Method Exploded

We need a way for our main method to communicate with the method we've written, and that's done through the return type and the list of parameters. The parameters indicate information that goes into the method, and the return type is the information that comes out of the method. You can have any number of parameters, but can only return one piece of information.

In this example, we have void as a return type - this indicates to Java that no information comes out of this method. We also have nothing between our brackets - this indicates that no information goes into the method. It is completely self-contained.

The sum of information that we provide regarding a method is known as the method definition.


Terminology Alert
Method Definition
The method definition is a combination of the method's name, return type, parameter list, and its modifiers (such as static and public).

This is a simple a method as we can write at this point - and its simplicity means that it can't do anything interesting. All it does is display some text to the console - and we already knew how to do that. So what's the big deal?

Well, let's look at a slightly more complicated example.

A More Complicated Method: Sending Information In

Our method shows the same method each time it is invoked at the moment - how about changing it so that we can change the message that is going to be displayed? For this we need to change our parameter list.

A parameter is made up of two parts - the type of the parameter, and the name we give that parameter within the method itself. We already see an example of a parameter in our main method, although we never do anything with it.


Terminology Alert
Parameter
A Parameter (also known as an argument) is a piece of information sent into a method when it is invoked. Parameters have two parts - a type (such as int, float, String) and a name.

The parameters are pieces of information that are passed from the invoking method into the method being invoked. We call this process parameter passing.


Terminology Alert
Parameter Passing
Parameter passing is the process of taking information and passing it into a method. We refer to pieces of information in this case as having been 'passed into the method'.


<source lang = "java"> public static void printMessageToConsole (String message) { </source>

Now whenever we invoke the method, we need to provide a string parameter or the program will refuse to compile. We do this by providing the information between the brackets at the invocation:

<source lang = "java"> printMessageToConsole ("They're eating Dave!"); </source>

Note here that we don't give the type of the parameter, only the value - Java knows what type it should be (it gets that from the method definition). If we don't provide a parameter, we'll get an error message when we compile:

F:\Projects\APE Tool\Teaching Material\chapter 08\MethodMonkey\MethodMonkey.java:10:
printMessageToConsole(java.lang.String) in MethodMonkey cannot be applied to ()
printMessageToConsole();

So, we provide our parameter, compile and run, and we see our console window again. Excellent, that seems to work.

Now, let's change what we have sent in as a parameter - that should change the text displayed to the console, right?

<source lang = "java"> printMessageToConsole ("Seriously, they're just chewing on his head!"); </source>

Alas, no - it is not to be:

13.3: Console Output

What gives? Well, let's look at our method again:

<source lang = "java">

   public static void printMessageToConsole(String message) {
       System.out.println("Wow, his brain is tiny.  That's an aperitif at best.  My head must look like an all you can eat buffet in comparison");
   }

</source>


See, regardless of what gets passed in as a parameter, it will always print the same text. We may have passed in a parameter, but Java doesn't know what to do with it. You need to tell it - and in this case, we want to print out the message that is passed in as a parameter:

<source lang = "java">

   public static void printMessageToConsole(String message) {
       System.out.println(message);
   }

</source>

And now, our method will print out the message we tell it to:

<source lang = "java">

import draconia.APE.pacman.*;

public class MethodMonkey {

   public static void main(String args[]) {
       PacmanGame main = new PacmanGame();
       main.setMap("blank");
       main.showGame();
       printMessageToConsole("I honestly don't want zombies chewing on my head.");
       printMessageToConsole("I'm going to go ahead and call that a sub-optimal outcome.");
       printMessageToConsole("Maybe if I tie up Pauline and leave her by the door, I can make my escape.");
   }
   public static void printMessageToConsole(String message) {
       System.out.println(message);
   }

}</source>


Voila:

I honestly don't want zombies chewing on my head.
I'm going to go ahead and call that a sub-optimal outcome.
Maybe if I tie up Pauline and leave her by the door, I can make my escape.

Pretty nice, eh?

Let's say we wanted to get a bit more adventurous - let's have our method print out the message a specific number of times - we just add another parameter to the method definition:

<source lang = "java"> public static void printMessageToConsole (String message, int repeat) { </source>

Notice that we separate each parameter (type and name) with a comma - we can add parameters indefinitely, the only restriction being that we cannot have two parameters with the same name in one list.

We also need to change the code a little bit:

<source lang = "java">

   public static void printMessageToConsole(String message, int repeat) {
       int i;
       for (i = 0; i < repeat; i++) {
           System.out.println(message);
       }
   }

</source>


And then alter the method invocation to take account of the new parameter:

<lang source = "java"> import draconia.APE.pacman.*;

public class MethodMonkey {

   public static void main(String args[]) {
       PacmanGame main = new PacmanGame();
       main.setMap("blank");
       main.showGame();
       printMessageToConsole("It's too late!  They're here!", 1);
       printMessageToConsole("AAAAAAAAAAAAAAAAAAAAAA!", 6);
   }
   public static void printMessageToConsole(String message, int repeat) {
       int i;
       for (i = 0; i < repeat; i++) {
           System.out.println(message);
       }
   }

}

</source>


Now we run the program, and we get the following output:


It's too late! They're here!
AAAAAAAAAAAAAAAAAAAAAA!
AAAAAAAAAAAAAAAAAAAAAA!
AAAAAAAAAAAAAAAAAAAAAA!
AAAAAAAAAAAAAAAAAAAAAA!
AAAAAAAAAAAAAAAAAAAAAA!
AAAAAAAAAAAAAAAAAAAAAA!

Huzzah! More power to us!

A Second Example

Okay, let's look at writing a method that's actually useful - the one we've done so far is a proof of concept, but doesn't do anything particularly helpful. Let's write a method that works with our pacman game, and it will just cause Pacman to turn around so he is facing the other way.

Remember that our method doesn't know anything about pacman - we need to provide it with the information if we want it to do something with it. The parameters that we pass into a method don't have to be the simple ones we've created ourselves - they can be anything. Including, for example, a pacman game!

Now, let's look at the basic outline structure of an APE program:

<source lang = "java">

import draconia.APE.pacman.*;

public class MethodMonkey {

   public static void main(String args[]) {
       PacmanGame main = new PacmanGame();
       main.setMap("blank");
       main.showGame();
   }

}

</source>

Look at the first line of the main method - it might be more complicated than the ones we've done in the past, but that's a variable declaration. We're creating a variable called 'main', and it's of type PacmanGame. That's all the information we need to write our turnPacmanAround method - we are going to pass in the main variable so that the method has something to work with:

<source lang = "java"> public static void turnPacmanAround (PacmanGame main) { </source>

And then all we do in the method is invoke the turnLeft method twice:

<source lang = "java">

   public static void turnPacmanAround(PacmanGame main) {
       main.turnLeft();
       main.turnLeft();
   }

</source>

And hola! Pacman turns around on command:

<source lang = "java">

import draconia.APE.pacman.*;

public class MethodMonkey {

   public static void main(String args[]) {
       PacmanGame main = new PacmanGame();
       main.setMap("blank");
       main.showGame();
       turnPacmanAround(main);
   }
   public static void printMessageToConsole(String message, int repeat) {
       int i;
       for (i = 0; i < repeat; i++) {
           System.out.println(message);
       }
   }
   public static void turnPacmanAround(PacmanGame main) {
       main.turnLeft();
       main.turnLeft();
   }

}

</source>

That opens up a lot of possibilities for us - possibilities we will discuss in the next case study.

The Return Type

Okay, so that's the parameter list. Now what about the return type?

The return type is the type of information that is given out from the method. In technical terminology, we return a value:


Terminology Alert
Return Value
The returned value is the information that comes out of a method.

We do this using a special keyword - return. We use the return keyword, and the value that we want to be returned from the method - and that's all we do. However, whenever we return a value from a method, we exit the method entirely. As soon as we return from a method, we are done - no code after the return statement will be executed.

The value that we return from a method must be stored somewhere, and we do that when we invoke the method - we tell Java what to do with the data that comes out of the method.

Let's look at an example of all of this in action - we'll write a method that prompts for a number between one and ten, and then validates the data that the user enters. We need our PacmanGame for this, because that's where our getIntFromUser method can be found. We also need to know what we are going to prompt the user with, so we also pass in a String.

For our return type, we are going to send back a validated number between one and ten - this is a whole number, and so the value we return will be of type int. That's what the return type of our method will be:

<source lang = "java"> public static int promptForNumber (PacmanGame main, String prompt) { </source>

And from this point on, we're just writing code like we've done before. We create a variable to hold the user's number:

<source lang = "java"> int num; </source>

And then prompt for it, using the prompt parameter for the text to display:

<source lang = "java"> num = main.getIntFromUser (prompt); </source>

Next, we need to check if the number is between one and ten. If it isn't, we set it to the most appropriate boundary value:

<source lang = "java"> if (num > 10) {

 num = 10;

} else if (num < 1) {

 num = 1;

} </source>

And finally, when we're done, we need to send the num variable back to the method that invoked our method, which is where we use our new return keyword:

<source lang = "java"> return num; </source>

That method in whole looks like this:

<source lang = "java">

   public static int promptForNumber(PacmanGame main, String prompt) {
       int num;
       num = main.getIntFromUser(prompt);
       if (num > 10) {
           num = 10;
       } else if (num < 1) {
           num = 1;
       }
       return num;
   }

</source>


Now, when we invoke the method, we need to store the value that comes out of it. The syntax for this shouldn't be surprising, since you've been doing that every time you prompted the user for information - we create a variable, and make it equal to whatever comes out of the method:


<source lang = "java"> int validatedNumber; validatedNumber = promptForNumber (main, "Enter a number"); </source>

Whenever we call this method, we will get a number between one and ten - and we can call it as many times as we like with as many different prompts as we like.

Method Overloading

As far as Java is concerned, a method is uniquely identified by a combination of its name, and the parameter list. This means that we can have multiple methods with the same name, as long as they have different parameter lists. Having two methods with the same name is known as method overloading. Consider for example if we had a second version of our turnPacmanAround method - one that turned him around a number of times specified by the developer:

<source lang = "java">

   public static void turnPacmanAround(PacmanGame main, int times) {
       int i;
       for (i = 0; i < times; i++) {
           main.turnLeft();
       }
   }

</source>

Now we have two methods called turnPacmanAround, but Java doesn't mind. It's quite happy with that. But how does Java know which one we mean when we try to invoke it?

Java distinguishes between methods of the same name based on their parameter list - the combination of the name and the parameter list is called a method's signature.


Terminology Alert
Method Signature
A method signature is the combination of the method's name, and the type and order of its parameters. So the first turnPacmanAround method has a signature of 'turnPacmanAround;PacmanGame' whereas the second has a signature of 'turnPacmanAround;PacmanGame,int'.

When you invoke a method, Java takes the list of information that you provide to the method and looks for one that has a signature that matches the information it has. If you provide just a PacmanGame, Java looks for the method that is looking for just a PacmanGame. If you provide a PacmanGame and a number, it looks for the one that has those two parameters. It executes only the method it finds that matches the signature.

For example, let's consider the following two invocations:

<source lang = "java">

       validatedNumber = promptForNumber(main, "You spin me right round baby,  right "
               + "round.  How many times?");
       turnPacmanAround(main, validatedNumber);
       turnPacmanAround(main);

</source>


Run the code, and it gets to the first invocation of turnPacmanAround. It takes the name of the method, and the parameters it has been given (a PacmanGame and an int) and looks through the methods it has for something that matches. It comes to the first turnPacmanAround method:

<source lang = "java">

   public static void turnPacmanAround(PacmanGame main) {
       main.turnLeft();
       main.turnLeft();
   }

}

</source>

'Hrm', thinks Java. 'I have a PacmanGame and an int, but this method only expects a PacmanGame. This is not the method for me, my quest must continue'.

Then it gets to the second one:

<source lang = "java">

   public static void turnPacmanAround(PacmanGame main, int times) {
       int i;
       for (i = 0; i < times; i++) {
           main.turnLeft();
       }
   }

</source>

'Ah-ha!', thinks Java. 'This expects a PacmanGame and an int, and that's exactly what I have! This is the method I must execute', and it then executes the code in that method (and only that method - it doesn't execute code in the other method).

When it gets to the second invocation, it follows exactly the same procedure, except this time it gets to the first method and thinks 'Hey, I have a PacmanGame only, and this expects a PacmanGame only! This is the method for me!', and then executes the code in that method.

The issue of method signature matching raises another restriction of Java - it bases the method signature on the type and order of the parameters, not the names. The following method declarations would give an error because Java has no way of distinguishing between them:

<source lang = "java">

   public static void turnPacmanAround(PacmanGame main) {
       main.turnLeft();
       main.turnLeft();
   }
   public static void turnPacmanAround(PacmanGame main) {
       main.turnRight();
       main.turnRight();
   }

</source>

This will give you the following error message:

F:\Projects\APE Tool\Teaching Material\chapter 08\MethodMonkey\MethodMonkey.java:33:
turnPacmanAround(draconia.APE.pacman.PacmanGame) is already defined in MethodMonkey
public static void turnPacmanAround (PacmanGame main) {

Method overloading is very useful, and we'll see an example of why when we get to talking about constructor methods in the next few chapters.

Why Use Methods?

So, that's how methods work - but why does it matter?

Well, it primarily comes into play in complicated programs that involve the same pieces of code being executed a number of times. If you have the same code repeated ten times, and horror of horrors you discover a bug in that code, you have to fix it ten times. If you want to enhance the functionality, you need to do so in ten places. This quickly becomes an immensely time-consuming and tiresome task, and is prone to errors - if you have to do the same thing ten times, who's to say you won't make a typo or logic error just out of sheer boredom of repetition? Such errors are often called transcription errors.

Methods make a program easier to maintain, easier to fix, easier to read, and easier to enhance. Make use of them at every opportunity you get!

Conclusion

Methods provide a neat way of breaking up common pieces of functionality into clearly defined packages, which allows you to group logical sections of a program together. This is a very powerful idea - well written programs exhibit the property of being modular, which means they fit together easily. If you want to change where you do your data validation, you don't need to rearrange all your code - you just change where the method is invoked. As we head into the next chapters, you'll start to find this idea becoming more and more relevant to the programs you write.