Lesson 4.1 Simple Methods
In Lesson 3 you learned about attributes--the data objects that you must define to store an object's state. In this lesson, you'll learn about methods--the actions you must define to carry out an object's behavior.
In this lesson you'll learn the "basics" about writing methods. You'll start with some background information, learning:
- what a method is
- where a method is defined
- where a method is used
- the difference between a method's interface and its implementation
- the different method relationships: inherited, overriddent, and overloaded
Once you've mastered method theory, you'll move on to hands-on method creation, taking a simple method example from definition to execution.
Finally, you'll take a look "under the hood" and see what really happens when you call a method. Along the way, you'll be introduced to the method call stack and your browser's Java console.
What is a Method?
When you want to tell an object to perform an action, we often say that you "send it a message" like this:
| myButton.setFont(bigFont); |
This line of code sends the setFont message to the object named myButton.
- The message name is setFont.
- The receiver of the message is the Button object named myButton.
To carry out your request, the Button object will use a method, and the method will be named setFont.
A method is code that performs some action. Every time you send a message to an object, that object must have either a method of the same name as the message, or one of its ancestor classes [superclasses] must. Thus, "sending a message" and "invoking (or calling) a method", usually mean the same thing, and I'll frequently use them interchangeably.
Where Methods Are Defined
In Java, all methods are defined inside the body of a class definition; Java has no free-standing methods that are not contained in a class.
|
No Free-standing Methods or Functions
|
| Although Java has no free-standing methods, you'll see that static methods fill much the same function as free-standing methods in other languages like C++. |
Where Methods are Used
Most methods are externally visible, that is, visible outside of the class where the method is defined. If a method is a public method, then you can tell an object to perform that particular action. Some methods, however, are only used internally, by other methods in the class.
Here's a short example:
public class Horse extends Animal
{
public void lead(Substance s)
{
// Code to lead the horse here
// If Substance looks inviting
// then call the drink() method
}
private void drink()
{
// Code to drink here
}
} |
Horse trigger = new Horse();
Substance water = new Substance("H20");
trigger.lead(water); // OK
trigger.drink(); // Not externally visible |
The externally visible methods of a class are called its public interface.
Interface vs Implementation
When you create a new class, you must decide which methods are visible to users of your objects and which methods are not.
|
Which Methods Should Be Visible?
|
| You may be tempted to make all methods visible, but you should resist such temptation. You should supply all the methods that are necessary to use the object, but no more. The more methods you make public, the harder your class will be to use. |
A computer program is an artifact, a human construction built to solve a problem. You don't need to know how an artifact works before you can use it. Most of us drive cars or listen to radios, all without understanding the internal combustion engine or Ohms Law or radio waves.
You do, however, need to know how to "operate" an artifact before you can use it. Before you can drive you need to learn to make your automobile go forward [press on the gas], stop [press on the brake], and turn left or right [turn the steering wheel].
The Public Interface
This “public face” of an artifact is its interface, the things that a user needs to know to use it. Here are some examples:
- Automobile: Steering wheel and gas pedal
- Radio: Tuning and volume knobs
- Computer: Mouse and keyboard
In programming, an object's interface consists of the public methods it contains. Your goal as a computer programmer should be to design interfaces that are complete [they'll do the things that a user normally wants to do], and minimal [they should not be cluttered with useless features].
Methods that are not required to use an object should be private methods. Such private methods [along with the attributes of each object] are called the object's implementation. Users of your class should not need to know about the internal workings of your implementation to create and use the objects you define.
Different Kinds of Methods
Some methods simply perform an action, require no extra information, and produce no values. In other languages, these sorts of methods may be called commands or procedures. You'll learn to write such simple methods in this section.
Often, methods produce a value when they are called. These kinds of methods can be used inside expressions, where the value they produce is returned to the code that invoked the method. In some languages these kinds of methods are called functions. You'll learn how to write such methods in Lesson 4.2, "Producing Values".
Finally, many methods require extra information that permits the method to customize its behavior. This extra information is passed to the method as one or more arguments. You'll learn how to write such methods in Lesson 4.3, "Arguments."
Method Relationships
There are three kinds of method relationships that you should be aware of.
Inherited Methods
First, every child class automatically inherits all of its parent's public methods. These inherited methods may be used just as if they were defined inside the child class.
In the example, [click to enlarge], the Chihuahua object named fred is sent the speak message, but the Chihuahua class doesn't contain a speak() method. Because the Chihuahua class is a subclass of the Dog class, and the Dog class does define a speak() method, you can tell fred to speak without any ill effects.
Overridden Methods
When a child class inherits a method from its parent [or other ancestor class], it is also free to create a new, and possibly different, action to be performed when it receives that particular message. Such methods are said to be overridden.
In the example shown here [click to enlarge], the Bird class has two subclasses, Duck and Crow. The Bird class contains a speak() method, which, as you might expect, displays the words "Tweet". Both Duck and Crow automatically inherit this behavior, but, feeling that it is, perhaps, a little undignified, they modify it significantly. The speak() method in both Duck and Crow is thus an overridden method.
Overloaded Methods
Both inherited methods and overridden methods depend on the inheritance relationship--they are relationships between methods in subclasses and superclasses. The third type of relationship does not require inheritance: overloaded methods.
An overloaded method is a method that has the same name as another method, in the same class or in an ancestor class, but that has a different number or order of arguments. [This combination or method name along with the number and order of arguments is called the method's signature.] You'll learn how to write overloaded methods in Lesson 4.4, "Overloading."
A Simple Method
Let's take a look at a class that contains and uses a simple method. After you've taken a quick look, we'll examine it in a little more detail:
import java.awt.*;
import java.applet.*;
public class Simple extends Applet
{
public void init()
{
showLuckyNumber();
}
public void showLuckyNumber()
{
Label aLabel = new Label("5");
add(aLabel);
}
} |
Inside the Simple Class
The Simple class contains just two methods, init(), with which you should already be familiar, and showLuckyNumber(), the new, simple method that we're interested in.
First, notice that a method is always defined inside the body of the class that contains it, never inside another method.
| There is one exception to this rule. If a method is defined inside an inner class, the definition may actually be defined inside a method.
|
Method Definition
When you define a method, the method definition has five parts:
1. Access specifier
The access specifier determines who can invoke this message. As you can see, showLuckyNumber() is public, so if I had a Simple object, I could call this method. If the access specifier were private, then only another method in the Simple class, such as init(), would be able to call the showLuckyNumber() method.
[Actually, since the only method that does call showLuckyNumber() is the init() method, showLuckyNumber() could be made private with no real effect at all.]
2. Return type
The return type is used for methods that produce a value, so that the method can be used in an expression. Simple methods don't return a value, so we use the "anti-type" named void.
3. Name
The name of our method is showLuckyNumber(). Method names must follow the rules for Java identifiers, just as attributes do. Here, we're also following the Java naming conventions, starting our method names with a lowercase letter and using uppercase letters to begin each embedded word.
4. Argument list
Because the simple methods we're discussing don't take arguments, the argument list is empty. You still have to include the parentheses, however.
5. Method body
The method body is enclosed in braces { }, following the argument list.
Calling Simple Methods
To call a simple method from within the class where it is defined, you just:
- use the name of the method
- followed by an empty set of parentheses
- followed by a semicolon
Here's the example of calling showLuckyNumber() from the Simple class init() method:
public void init()
{
showLuckyNumber();
} |
|
Simple Method Reminders
|
| You cannot use simple methods as part of an expression, like you can when the method produces a value.
Make sure you understand the difference between defining a method and calling a method.
A method definition:
- Always has a return type before the name
- Never ends in a semicolon
Calling a method:
- Never has a return type before the name
- Always ends in a semicolon
|
How Method Calls Work
When you call a method from another method, the caller [the method making the call] saves the location of the line of code following the method call before tranferring control to the new method.
In the new method, each statement is executed in order until the method is finished [either by running out of statements to execute, or by encountering the return keyword]. When the method is finished, it returns control to the statement following the original method call--the location originally saved by the caller.
This is slightly complicated by the fact that the method you're calling may, itself call other methods, which may also call other methods in turn. This chain of method calls is known as the "call stack" and is illustrated here.
Examining the Call Stack
Let's take a look at the call stack in real life by creating a new applet, called TwoMethods, that contains an intentional error. Here's how it will work:
- First, add an uninitialized Label attribute named aLabel
- Next, add your init() method. In your init() method simply call the method named, appropriately, firstMethod().
- Now, add firstMethod(), a simple method that just calls secondMethod().
- Finally, add secondMethod() which attempts to add the uninitialized Label reference, aLabel, to the applet.
Your code should look like this:
import java.awt.*;
import java.applet.*;
public class TwoMethods extends Applet
{
Label aLabel; // Uninitialized
public void init()
{
firstMethod();
}
public void firstMethod()
{
secondMethod();
}
public void secondMethod()
{
add(aLabel); // Oops!
}
}
|
What happens?
I've compiled and added a copy of this applet to this Web page, immediately following the listing. When you open this page, your browser attempts to run the program, and it creates an error.
In Communicator, you'll see something like this in your Web browser's status bar:
We can try to examine the error by opening the Java console from the Communicator window, like this:
Once there, you'll see something like this. This is the call stack:
The top of the call stack shows you what error occured. In this case, the error was a java.lang.NullPointerException. [As you gain some experience with Java, you'll learn to interpret such messages as "Uninitialized Variable"]
Immediately below the error description is the actual call-stack: the methods that were in memory at the time the error occurred. As you can see, the error occurred in a method called addImpl() which was called from a method called add(). You notice that both of these methods are in the java.awt package--this is not code you've written.
The next line, however, should look familiar. It says that the add() method was called from the method secondMethod() in the class TwoMethods. Following that, you can see that secondMethod() was called from firstMethod() and that firstMethod() was called from init(). At that point, you can see that you've disappeared into some Netscape internal code.
Where are the Line Numbers?
Both Netscape and IE use a "just-in-time" compiler [JIT] to turn your Java byte-codes into native code. When this occurs, you lose the ability to discover which line-number your error occured on. The Java console in early versions of MS Internet Explorer retained the line number information, and so were a little more helpful when debugging your application. Current versions of the Microsoft JVM do not retain the line numbers.
You can then start the Java console from the IE View menu like this:
|
No IE Java Console?
|
| If you don't see the Java Console on the View menu, then you'll need to open the Internet Options dialog [on the View menu in IE4, and on the Tools menu in IE5]. On the Advanced tab, locate the checkbox entitled "Java console enabled" and check it. After that, you'll have to restart your browser to see the menu command. |
If you use the Java2 plugin, it has its own Java Console, which you can access by right-clicking the small Java icon in the System tray:
In the Java2 Plugin Java Console you can see the line numbers from which each method was called. When you encounter an error like this, start at the top of call stack, and read down until you come to some of your own code.
In this case, you can see that the first instance of our code is secondMethod() and that the error occurred on line 20 of the file TwoMethods.java. Tracing back, you can see that secondMethod() was called from the method firstMethod() line 15, and that firstMethod() was called from the init() method at line 10. After that, just as with the Netscape Java console, the trail turns cold as we trace into some proprietary Sun applet code.
Using the call stack and its line numbers in conjunciton with the line numbers shown in your text editor, you can pinpoint the errors that occur in your code.
Something to Talk About
Here's something to try. Download TwoMethods.java to your own computer. Compile and run it under the appletviewer. [You'll have to create a small HTML file.]
- What does the call stack look like there?
- What change do you have to make to get TwoMethods to run correctly?
Please continue to the next section of this lesson.
|