Java Programming   Java Programming
 |Sofia Home | Content Gallery |
Home
Syllabus
Schedule
Lessons
Assignments
Resources

Lesson 4.4 Other Methods

Overloaded and static Methods

Up to now, every method you've created has had a unique name. Each applet you create contains a single method named init(), for instance. Also, each method has been attached to a particular object. If you send a message like this:

myLabel.setText("Hi Mom");

it only affects the Label object named myLabel; all other Label objects remain as they were.

In this section, that will change. You'll learn how to create a multitude of methods, all of them with the same name. These are called overloaded methods. You'll also learn how to create methods that work without the need to first create an object. With these static methods, you'll be able to send messages to a class, instead of to an object.

Let's start by looking at overloaded methods.

Overloaded Methods

You might be surprised to find out that the same class can have two methods that have the same name. In fact, you can have several methods, in the same class, all sharing the same name; it's quite common. Such methods are called overloaded methods.

Here is the basic rule that determines how such methods can be created and which particular version of a method will be called:

  • Methods in the same class may have the same name, if:
    1. The number of their formal arguments differ
    2. The type or order of their arguments differ

The combination of method name, formal argument type, number, and order, is called the method’s signature, and it acts like a DNA code to uniquely identify a particular method.

Back to Top

Overloaded Examples

Here is a class with several methods, all having the same name. Take a look at the different method signatures, and, using the rules you've just learned, decide which methods are legal, and which methods are illegal.

public class TestIt
{
  public void myMethod() { }
  public void myMethod(int a) { }
  public void myMethod(int a, int b) { }
  public void myMethod(int c, int d) { }
  public void myMethod(int a, byte b) { }
}

Of the five methods here, numbers 1, 2, and 5 are legal, while number 3 and 4 are not, because they have the same number and type of arguments.

Return Types

When looking at the method signature, Java does not consider the return type of the method. Java will not permit two methods in the same class that have identical signatures, but different return types.

Because of this, the following two methods could not co-exist in the same class:

int    doIt(int a) { }
double doIt(int b) { }

Calling Overloaded Methods

When calling a method, Java looks at the actual arguments to decide which of several overloaded methods to call. The rules for argument matching can become quite baroque, but here's the simple version. 

For this example, we'll use a version of the TestIt class with the third method removed. We'll call this class TestIt2, as shown here. Let's assume as well, that you have created a TestIt2 object like this:
TestIt2 t = new TestIt2();

 

public class TestIt2
{
  public void myMethod() { }
  public void myMethod(int a) { }
  public void myMethod(int c, int d) { }
  public void myMethod(int a, byte b) { }
}

Here are the rules that will apply when you try to call any of the methods in the TestIt2 class using your object t.

Rule 1: Number of arguments

Java first tries to match the number of actual arguments with a method that has the same number of formal arguments. If it cannot find a match, then it gives up as an error. Thus the method call:

t.myMethod(10, 20, 30);

fails, because there are no methods that take three arguments.

Rule 2: Exact type matches

Once Java has located a method [or methods] having the correct number of formal arguments, it compares the type of each actual argument to the corresponding formal argument. If an exact match can be found, then Java calls the method. Thus the method call:

t.myMethod(10, 20);

exactly matches the fourth method, because both arguments are of type int.

Rule 3: Acceptable conversions

If Java cannot match the exact types of arguments, it looks for acceptable conversions, preferring the conversions that are closest to the actual argument type. Java will not, however, make conversions where data will be lost. Thus the call:

t.myMethod(10, 1.5);

will fail because 1.5 cannot be converted to either an int or a byte, the only two options for the second argument, given the available methods.

Back to Top

Overloading II: Why Bother?

Why bother with method overloading? Doesn't it seem like having multiple methods, all with the same name, would be confusing? How are you supposed to remember which method is being called?

All of these are valid arguments against using overloaded methods. Simply giving all of your methods the same name without ample planning and forethought really will make your programs harder to understand and maintain.

Making Easy-to-use Methods

Properly used, however, method overloading allows you to write methods that are much easier to use than those that don't employ overloading. Here's an example.

Suppose that 90% of the buttons in your applet will always use a blue, 24 point font, but that 5% will require a 30 point blue font and the remainder of the buttons require fonts in varying sizes and colors.

The Generic Solution

To meet these requirements you could use the generic makeButton() method which you wrote in the last lesson. If you do so, it means that for all of your buttons you'll have to write this

Button b = makeButton("String", 24, Color.blue);

even though you know that 90% of the time you will always be using 24 and Color.blue as the last two arguments.

Uniquely-named Methods

Another option would be to write multiple, uniquely-named methods, like make24ptBlueButton() and make30ptBlueButton(), and have each of these methods call makeButton() passing the correct arguments.

Overloaded Methods

Finally, a third option would be to write three different methods, but to name all three methods makeButton(). For the vast majority of your buttons--those blue, 24 point ones--you could call the method defined like this:

Button makeButton(String s) { }

For those times when you need a blue button, but in a different size, you can call the method defined like this:

Button makeButton(String s, int fontSize) { }

Finally, for the time when you really need 10 point yellow buttons, you can use the "full-featured" version of makeButton() which is defined like this:

Button makeButton(String s, 
                  int fontSize, 
                  Color bColor) { }
Back to Top

Defining makeButton()

It might seem like this is alot of extra work, just to make calling a few methods a little simpler. That turns out, however, not to be the case. Once you have defined the "full-featured" version of makeButton(), the other versions just call that version to get their actual work done. Both methods are only a single line long.

Here is the single-argument version of makeButton():

public Button makeButton(String text) 
{
  return makeButton(text, 14, Color.blue);
}

Here is the two-argument version:

public Button makeButton(String text, int fontSize) 
{
  return makeButton(text, fontSize, Color.blue);
}

Using makeButton()

Using these overloaded makeButton() methods is much easier on the users of your class than having them try to remember the names of several different button-creation functions, or having them pass three arguments for every button, especially when most of the buttons use most of the same arguments.

Furthermore, when you use overloaded functions like this, if your manager decides that those 90% of your buttons should have been green instead of blue, you only have to change one function. If you used the generic makeButton() to create your buttons, you'll have to change the actual arguments used for every button.

Here's an applet, MakeButtons.java, that uses these three versions of makeButton() to create different buttons like this:

Button b1 = makeButton("Normal");
Button b2 = makeButton("Bigger", 30);
Button b3 = makeButton("Custom", 18, Color.red);

Back to Top

Static Fields and Methods

A static object is one that persists as long as your program is running. In Java, we have both static fields and static methods.

Static Fields

Static fields are shared fields. You can think of them as semi-global variables, shared among all the objects in a class. A static field is different than a regular field, because every time you create an object, each object gets its own copy of all of the regular fields, but it does not get copies of the static fields.

A "population counter" is a good example of a static field. If you write a "Space-Wars" type game, you might have several different kinds of playing pieces--fighters, battle-cruisers, cargo ships, and so forth. In your game, you need to keep track of how many pieces of each type are currently active. 

"Global" Counters

The straight-forward way is to put a counter for each possible type in your game class like this:

public class SpaceWars extends ... 
{
  int numCruisers = 0;
  int numFighters = 0;
  int numCargoShips = 0;
  ...
}

The problem with this solution, is that it is not very flexible. Everytime you add a new playing piece you must update your SpaceWars class and its counters as well. It would be nice if you could just ask the pieces themselves how many exist.

If you attempt to accomplish this by putting a counter inside each of your "ship" classes, you immediately encounter a problem. Whenever you create a new ship, it has no way of telling how many other ships exist. 

Back to Top

Static Counters

The solution, is the user of a static field like this:

public class XFighter extends Fighter
{
  static int numXFighters = 0;
  // Remaining attributes & methods
}

Now, every time an XFighter object is created it can increment this counter [and when it is destroyed, it can decrement it]. All of the XFighter objects will share access to this one field.

How to Increment numXFighters
You may be wondering how the XFighter class knows just when an XFighter object has been created. The answer lies in constructors. Remember that when you create a Button, you use the Button constructor to create it. You'd create XFighter objects in the same way, by calling the XFighter constructor. In Lesson 9, when you learn to write your own constructors, you'll see how such a population counter is implemented

Static Methods

Adding a static field to the XFighter class looks like a good solution. Now the XFighter class--and supposedly the OFighter class and all the rest--each keeps track of its own inventory.

To find out how many XFighters there are, you can give each class a method called getNumPieces() and use it like this:

XFighter x = new XFighter(); // Temporary fighter
int numX = x.getNumPieces() - 1;
x.destroyFighter();

Of course, it looks kind of clunky to have to subtract 1 from the number of pieces, just because you had to create the temporary XFighter object named x to find out how many there were. Then, you have to destroy the temporary XFighter, otherwise the count will be off the next time. But destroying the XFighter creates its own problems because now your kill-score is off.

Anyway, you get the idea. You really want to find out how many XFighters exist at any time, without having to create a temporary XFighter object. As with the previous problem, the solution is simple: use static methods.

A static method is just like a regular method with two exceptions:

  1. A static method can only access static fields. It can never access the regular fields in a method. Likewise, it can only call other static methods; it cannot call regular methods. [The reverse is not true. A regular method can call static methods and it can use static fields.]
  2. A static method does not require an object to invoke [call] it. Instead, you use the name of the class--rather than the name of an object--as the message receiver.

Here's what the static getNumPieces() method would look like in the XFighter class:

public class XFighter extends Fighter
{
  static int numXFighters = 0;

  public static int getNumPieces()
  {
    return numXFighters;
  }

  // Remaining attributes & methods
}

One you've done that, you can call the XFighter getNumPieces() method from your SpaceWars class like this:

int num = XFighter.getNumPieces();

Something to Talk About

Here's something to try! Download the applet MakeButtons.java, compile and run it. How can you change the applet so that the "normal" buttons are always green, rather than blue?

Please continue to the next section of this lesson.

 

Back to Top

 

Content Developed by Stephen Gilbert, Licensed under a Creative Commons License
Published by the Sofia Open Content Initiative
© 2004 Foothill-De Anza Community College District &The William and Flora Hewlett Foundation