3.5. Creating MethodsWe had to send many messages to our Turtle object just to draw two squares. Do you notice any similarities in how we draw the squares? Each time we draw a square we turn right and go forward by 30 steps for a total of four times. It would be nice to name the list of steps for drawing a square and then just do the list of steps when a turtle is asked to draw a square. We do this by creating a method that knows how to draw a square. Methods are named blocks of commands that are defined inside a class definition. Once we have defined a method and successfully compiled the class definition, the objects of the class will respond to a message with the same name and parameters as the new method. So if we want Turtle objects to understand the message drawSquare(), we define a method drawSquare().
You have seen how to declare variables in Java: type name; or type name = expression;. To declare a method in Java use: visibility type methodName(parameterList) The structure of how you declare a method is referred to as the syntaxthe words and characters that have to be there for Java to understand what's going on, and the order of those things. A method declaration has a visibility (usually the keyword public), the type of the thing being returned from the method, the method name, and the parameter list in parentheses. This is usually followed by a block of statements which is an open curly brace followed by a series of statements followed by a close curly brace. The statements in the block will be executed when the method is invoked.
To declare a method that will draw a square we can use: public void drawSquare() { // statements to execute when the method is executed } The visibility in this method declaration is public. Visibility means who can invoke the method (ask for the method to be executed). The keyword public means that this method can be invoked by any code in any class definition. If the keyword private is used then the method can only be accessed from inside the class definition. You can think of this as a security feature. If you keep your journal on the Web (a blog), then it is open and anyone can read it. If you keep it hidden in your room then it is private and hopefully only you can read it. The return type in this method declaration is void. The return type is required and is given before the method name. If you leave off a return type you will get a compiler error. If your method returns a value the return type must match the type of the value returned. Remember that types can be any of the primitive types (char, byte, int, short, long, float, double, or boolean) or a class name. Methods that don't return any value use the Java keyword void for the return type in the method declaration. The method name in this declaration is drawSquare. By convention method names start with a lowercase letter and the first letter of each additional word is uppercase: drawSquare. Another example method name is turnRight. A method must have parentheses following the method name. If any parameters are passed to the method then they will be declared inside the parentheses separated by commas. To declare a parameter, you must give a type and name. The type can be any primitive type or class name. The name can be used by the code in the body of the method to refer to the passed value. We create a collection of statements by defining a block. A block is code between an open curly brace '{'and a close curly brace'}'. The block of commands that follow a method declaration are the ones associated with the name of the method (function) and are the ones that will be executed when the method is invoked. Most real programs that do useful things require the definition of more than one method (function). Imagine that in the definitions pane you have several method declarations. How do you think Java will figure out that one method has ended and a new one begun? Java needs some way of figuring out where the method body ends: Which statements are part of this method and which are part of the next? Java uses curly braces to do this. All statements between the open curly brace and close curly brace are part of the method body.
We can now define our first program (method)! Open Turtle.java by clicking on the Open button near the top of the window and using the file chooser to pick "Turtle.java". Type the following code into the definitions pane of DrJava before the last closing curly brace '}' (which ends the class definition). When you're done, save the file and click the Compile All button near the top of the window (Figure 3.8). Figure 3.8. Defining and executing drawSquare().![]()
Program 2. Draw a Square |
![]() public void drawSquare() { this.turnRight(); this.forward(30); this.turnRight(); this.forward(30); this.turnRight(); |
|
Notice that we changed turtle1.turnRight(); to this.turnRight();. The variable turtle1 isn't defined inside the method drawSquare(). Variables names are known in a context (area that they apply). This is also known as the scope of a variable. The variables that we define in the interactions pane are only known in the interactions pane, they aren't known inside methods. We need some other way to reference the object that we want to turn. Object methods are implicitly passed a reference to the object the method was invoked on. You can refer to that current object using the keyword this.
Compiling a Java class definition "Turtle.java" will produce a "Turtle.class" file. Compiling translates the Java source code which is in a format that humans understand into a format that computers understand. One of the advantages to Java is that the ".class" files aren't specific to any particular type of computer. They can be understood by any computer that has a Java run-time environment. So you can create your Java source code on a Window's based computer and run the compiled code on an Apple computer.
|
This code creates a method with the name drawSquare that takes no parameters and whenever the method is executed it will execute the statements inside of the open and close curly braces. It is a public method. It doesn't return anything so it uses the keyword void to indicate this. This method must be called on an object of the Turtle class. The this is a keyword that refers to the object this method was invoked on. Since this method is defined in the Turtle class the keyword this will refer to a Turtle object.
Once the method has successfully compiled you can ask for it to be executed by sending a message to a Turtle object with the same name and parameter list as the method. Click on the INTERACTIONS tab in the interactions pane (near the bottom of the window). This method doesn't take any parameters, so just finish with the open and close parentheses and the semicolon. When you compile the interactions pane will be reset, meaning that all the variables we have defined in the interactions pane will no longer be understood. We will need to create a World and Turtle object again.
> World world1 = new World(); > Turtle turtle1 = new Turtle(world1); > turtle1.drawSquare();
When you invoke the method drawSquare() on the Turtle object referenced by the variable turtle1, the Java Virtual Machine has to find the method to execute. Methods are defined inside of a class definition so we defined drawSquare inside of the class Turtle by editing the file Turtle.java. Next we compiled the source file Turtle.java which created a Turtle.class file which contained the code for the Turtle class in byte codes for the Java virtual machine.
The first time you use a class the Java Virtual Machine loads the compiled class definition (the Turtle.class file) and creates an object that contains all the information about the class including the code for the methods. Every object has a reference to the object that defines its class as shown by Figure 3.9. The object that defines a class is an object of a class named Class. You can get the Class object using the method getClass().
> System.out.println(world1.getClass()); class World > System.out.println(turtle1.getClass()); class Turtle
The Java Virtual Machine will check for a method with the same name and parameter list in the object that defines the class (an object of the class Class) and if it is found it will execute that method. This means that the statements in the body of the method will be executed starting with the first statement. The object that the method was invoked on is implicitly passed to the method as well and can be referred to using the keyword this.
What if we want to draw a larger or smaller square? We could change each of the this.forward(30); lines to the new width and height and then compile. But, it would be easier to declare a variable in the method that would represent the width of the square and then use that variable name as the amount to go forward by like this: this.forward(width);. Then if we want to change the size of the square we only have to change 1 line. You can declare a variable anywhere in the body of a method but you must declare it before you use it. The name will be known and the value substituted for each occurrence of the name in the rest of the method. But the name will only be known inside the method it is declared in.
We can't have two methods with the same name and the same parameter list so we need a new name for this method. We simply named it drawSquare2 to show that it is the second version. We can copy the first method and paste it and rename it and then change it to declare and use the width variable.
![]() public void drawSquare2() { int width = 30; this.turnRight(); this.forward(width); this.turnRight(); this.forward(width); this.turnRight(); this.forward(width); this.turnRight(); this.forward(width); } |
Compile and run this method and check that you get the same results as with drawSquare().
> World world = new World(); > Turtle turtle1 = new Turtle(world); > turtle1.drawSquare2();
This is a bit better than the first version and a bit easier to change. But, you still have to recompile after you change the width to draw a larger or smaller square. Wouldn't it be nice if there was a way to tell the method what size you want when you ask for the method to be executed by sending a message that matches the method? Well you can! That is what the parameter list is for.
We can make the width of the square a parameter. Remember that if a method takes a parameter you must list the type and name for the parameter in the parameter list. What type should we use for width? Well, in the second version we used int because the turtle only takes whole steps not fractional ones so let's use that. What should we call this method? We could call it drawSquare3(int width) but someone may think this means it draws a square with a width of 3. We could call it drawSquareWithPassedWidth(int width) but that is rather long and you can tell it takes a passed width by looking at the parameter list. How about if we just call it drawSquare(int width)? You may think that isn't allowed since we have a method drawSquare(), but that method doesn't take any parameters and our new method does. Java allows you to use the same method name as another method as long as the parameter list is different. This is called method overloading.
![]() /** * Method to draw a square with a width and height * of some passed amount. * @param width the width and height to use */ public void drawSquare(int width) { this.turnRight(); this.forward(width); this.turnRight(); this.forward(width); this.turnRight(); this.forward(width); this.turnRight(); this.forward(width); } |
Type in the new method declaration and compile. Let's try this new method out.
> World world1 = new World(); > Turtle turtle1 = new Turtle(world1); > turtle1.drawSquare(200);
When you execute turtle1.drawSquare(200); you are asking the object referred to by the variable named turtle1 to execute a method named drawSquare that takes an integer parameter. The method drawSquare will use 200 for the parameter width everywhere it appears in the method drawSquare, with the result shown in Figure 3.10. The parameter name width is known throughout the body of the method. This is very similar to drawSquare2() but has the advantage that we don't need to change the method and recompile to use a different width.
An important reason for using parameters is to make a method more general. Consider method drawSquare(int width). That method handles the general case of drawing a square. We call that kind of generalization abstraction. Abstraction leads to general solutions that work in lots of situations.
|
Defining a method that takes input is very easy. It continues to be a matter of substitution and evaluation. We'll put a type and name inside those parentheses after the method name. The names given inside the parentheses are called the parameters or input variables.
When you evaluate the method, by specifying its name with input values (also called the arguments) inside parentheses, such as turtle1.drawSquare(20); or new Turtle(20,30,world1), each parameter variable is set to a copy of the argument value. This is called pass by value. All arguments in Java are passed by making a copy of their value. Does this mean that we make a copy of the World object when we pass it as a parameter? No, we just make a copy of the object reference which means we make another reference to that World object.