Espresso: A Concentrated Introduction to Java


Static Methods

Summary: We consider Java's static methods, one key mechanism for encapsulating code.

Prerequisites: Java basics. Input and Output. Numbers.

Contents:

Methods

Soon after beginning programming, most programmers discover that they often rewrite the same (or similar) code again and again and again. For example, beginning Java programmers regularly find that they have to write something like the following every time they want to read a number:

  pen.print("Enter a number: ");
  pen.flush();
  String userinput;
  userinput = eyes.readLine();
  int val = Integer.parseInt(userinput);

Of course, the code is not always identical, as they may use different prompts (or may not prompt at all) and may choose different names for the BufferedReader (eyes, in this case) and other variables.

In reaction to the need to repeat (and parameterize) similar code, most languages support subroutines, which are little more than parameterized blocks of code. In Java, we refer to subroutines as methods. Java supports two kinds of methods, object methods, which require an object to execute, and static methods (also called class methods), which do not require an object. For example, since the readLine method of the BufferedReader class requires a particular BufferedReader, it is an object method. In contrast, since the parseInt method does not require an individual object (we simply write Integer.parseInt), it is a static method.

Invoking Methods

You have already seen how you invoke (or call) a method in Java. For an object method, you write the name of the object, the name of the method, an open paren, a list of parameters separated by commas, and a close paren.

In generic form, an object method call looks like the following.

objName.methodName(param0,param1,...param.n)

For example,

pen.println("Hello");

You've also seen that methods can return values that you then assign or use in other ways. For example,

String combination = "** ".append(name).append(" **");

For a static method, you use a similar form, except that you use the name of the class in place of the name of the object. In the generic form, a static method call looks like the following.

ClassName.methodName(param0,param1,...param.n)

For example,

  int val = Integer.parseInt(userinput);

Writing Static Methods

You know how to call methods, but how do you write one? Since you do not yet know how to write your own classes, in this reading we will focus on the static methods. What do we need to specify when writing a method?

Since Java likes to check types, we must also specify

Since Java requires programmers to explicitly acknowledge whether or not a method can fail, you may have to indicate that the method can fail. When you start writing methods, it's easiest to assume that every method can fail.

Java also requires you to associate a protection level with each method. For now, we'll make each method public.

Java puts all of these components together into the following form.

protection staticVsObject returnType methodName(type0 name0, type1 name1 ... typen namen)
  optional_note_of_erroneousness
{
  body
} 

For your first static functions, you should use the following form for static methods.

public static returnType methodName(type0 name0, type1 name1 ... typen namen)
  throws Exception
{
  body
}

If your method does not return a value, use void as the return type.

For example, here is a simple method that prints and flushes a string, as we might do for a prompt. Such a method requires the PrintWriter for printing and the String to print.

public static void prompt(PrintWriter pw, String str)
  throws Exception
{
  pw.print(str);
  pw.flush();
} // prompt(PrintWriter, String)

Once we have defined such a method (say, in class Calculator), we can replace

  pen.print("Enter a number: ");
  pen.flush();

with

  Calculator.prompt(pen, "Enter a number: ");

When a method computes a result, we must use the command return to return the computed value. For example, here is a static method that reads and returns an integer.

public static int readInt(BufferedReader br)
  throws Exception
{
  String str = br.readLine();
  int i = Integer.parseInt(str);
  return i;
} // readInt(BufferedReader)

We can also express this more concisely as

public static int readInt(BufferedReader br)
  throws Exception
{
  return Integer.parseInt(br.readLine());
} // readInt(BufferedReader)

The verbose code from the beginning of the lab can now be phrased as

  Calculator.prompt(pen, "Enter a number: ");
  int val = Calculator.readInt(eyes);

In fact, we might even take the time to write a promptForInt method that does both

public static int promptForInteger(PrintWriter pw, BufferedReader br, String prompt)
  throws Exception
{
  pw.print(prompt);
  pw.flush();
  return Integer.parseInt(br.readLine());
} // promptForInteger(PrintWriter, BufferedReader, String)

The verbose code is now down to one line

  int val = Calculator.promptForInteger(pen, eyes, "Please enter a number: ");

Recursion in Static Methods

As you should have learned in a prior class, once you know how to write conditionals and subroutines, you can repeat code an arbitrary number of times using the technique of recursion. At its heart, recursion is simply the idea that a method can call itself. The body of a recursive method usually looks something like the following

public static type method(params)
  throws Exception
{
  if (test_for_base_case) {
    return base_value;
  }
  else {
    return computation(params, method(modify(params)));
  }
} // method(params)

For example, the famous factorial function is typically defined as

For this function

Putting it all together, we get

public static int fact(int n)
  throws Exception
{
  if (0 == n) {
    return 1;
  }
  else {
    return n * fact(n-1);
  }
} // fact(int)

We can also have different recursive calls. For example, one relatively efficient way of computing xn for non-negative integer n is to use the following rules

We can express those rules as an algorithm in Java as follows

public static BigInteger expt(BigInteger x, int k)
  throws Exception
{
  if (0 == k) {
    return BigInteger.ONE;
  } // k is 0
  else if ((k % 2) == 0) { // k is even
    BigInteger tmp = expt(x, k/2);
    return tmp.multiply(tmp);
  } // k is even
  else { // k is odd
    return x.multiply(expt(x, k-1));
  } // k is odd
} // expt(BigInteger, int)

We can even use recursion to check user input. For example, consider the problem of forcing the user to enter a positive number. One way to solve that problem is to read a number and, if the user fails to enter a positive number, try again. We can phrase that recursively in Java as

public static int readPositiveInteger(PrintWriter pr, BufferedReader br)
{
  pr.print("Please enter a positive integer: "); pr.flush();
  int i = Integer.parseInt(br.readLine());
  if (i > 0) {
    return i;
  }
  else {
    br.print(i)
    br.println( " is not positive.  Try again.");
    return readPositiveInteger(pr, br);
  }
} // readPositiveInteger(PrintWriter, BufferedReader)

Grouping Static Methods in Utility Classes

At this point, you're probably saying to yourself something like

I certainly understand the importance of subroutines in clarifying and shortening my code, but it really seems like I'll want to use the same subroutines again and again in different programs. How can I do that?

Fortunately, the answer to that question is relatively simple. You've seen that we can call static methods by giving the class name and the method name, so we can group our utility methods in a single class (or, perhaps, a few classes) and refer to them using the name of the class. Since the purpose of that class is to group static methods, that class, unlike those you've written so far, will not need a main method. We call classes whose main purpose is to provide utility methods utility classes.

In the laboratory, you will create utility classes for input/output and for some calculations.

Overloading Method Names

In some languages, once you have chosen a name for a method, no other method can have the same name. In Java, that restriction is loosened in a number of ways. As you've already seen, it is perfectly acceptable to repeat a method name used in another class, since whenever you call a method, you specify the object or class that supplies the method.

But Java also provides another mechanism for repeating method names: Java permits you to create more than one method with the same name as long as the methods have different parameter types. This technique, called overloading permits programmers to choose a clear name and not worry about making variations of that name. You have already seen overloading in methods you used. For example, there are at least two versions of the indexOf method for strings, one that takes a string as a parameter and one that takes a string and a starting index as the parameter.

No special syntax is required to overload method names.

History

Wednesday, 8 February 2006 [Samuel A. Rebelsky]

Thursday, 9 February 2006 [Samuel A. Rebelsky]

Friday, 10 February 2006 [Samuel A. Rebelsky]


This page was generated by Siteweaver on Thu Mar 30 15:24:32 2006.
The source to the page was last modified on Sun Feb 26 21:46:30 2006.
This page may be found at http://www.cs.grinnell.edu/~rebelsky/Espresso/Readings/static-methods.html.

You may wish to validate this page's HTML ; Valid CSS! ; Check with Bobby

Samuel A. Rebelsky
rebelsky@grinnell.edu