Chapter 7
IN THIS CHAPTER
Introducing static methods
Seeing some good reasons to use methods in your programs
Creating methods that return values
Creating methods that accept parameters
In Java, a method
is a block of statements that has a name and can be executed by calling
(also called invoking)
it from some other place in your program. You may not realize it, but you’re already very experienced in using methods. To print text to the console, for example, you use the
println
or
print
method. To get an integer from the user, you use the
nextInt
method. To compare string values, you use the
equals
or
equalsIgnore Case
method. Finally, the granddaddy of all methods —
main
— contains the statements that are executed when you run your program.
All the methods you’ve used so far (with the exception of
main
) have been defined by the Java API and belong to a particular Java class. The
nextInt
method belongs to the
Scanner
class, for example, and the
equalsIgnoreCase
method belongs to the
String
class. By contrast, the
main
method belongs to the class defined by your application.
In this chapter, you find out how to create additional methods that are part of your application’s class. Then you can call these methods from your
main
method. As you’ll see, this technique turns out to be very useful for all but the shortest Java programs.
The use of methods can dramatically improve the quality of your programming life. Suppose that the problem your program is supposed to solve is complicated, and you need at least 1,000 Java statements to get ’er done. You could put all those 1,000 statements in the
main
method, but it would go on for pages and pages. It’s better to break your program into a few well-defined sections of code and place each of those sections in a separate method. Then your
main
method can simply call the other methods in the right sequence.
Or suppose that your program needs to perform some calculation, such as how long to let the main rockets burn to make a midcourse correction on a moon flight, and the program needs to perform this calculation in several places. Without methods, you’d have to duplicate the statements that do this calculation. That approach is not only error-prone, but also makes your programs more difficult to test and debug. But if you put the calculation in a method, you can simply call the method whenever you need to perform the calculation. Thus methods help you cut down on repetitive code.
Another good use for methods is to simplify the structure of your code that uses long loops. Suppose you have a
while
loop that has 500 statements in its body. That structure makes it pretty hard to track down the brace that marks the end of the body. By the time you find it, you probably will have forgotten what the
while
loop does. You can simplify this
while
loop by placing the code from its body in a separate method. Then all the
while
loop has to do is call the new method.
Well, phooey. They’re right, but so what? I get to the object-oriented uses for methods in Book 3
. There, you find out that methods have a far greater purpose than simply breaking a long
main
method into smaller pieces. Even so, some of the most object-oriented programs I know use methods just to prevent repetitive code or to slice a large method into a couple of smaller ones. So there.
All methods — including the
main
method — must begin with a method declaration.
Here’s the basic form of a method declaration, at least for the types of methods I talk about in this chapter:
public static return-type method-name (parameter-list)
{
statements
…
}
The following paragraphs describe the method declaration piece by piece:
public
means and see some alternatives to
public
that are useful in various and sundry situations.main
method must always be static, and any other methods in the class that contains the
main
method usually should be static as well.static
comes the return type,
which indicates whether the method returns a value when it is called and, if so, what type the value is. If the method doesn’t return a value, specify
void
. (I talk more about methods that return values later in this chapter, in the section “Methods That Return Values
.”)
method-name:
Now comes the name of your method. The rules for making up method names are the same as the rules for creating variable names: You can use any combination of letters and numbers, but the name has to start with a letter. Also, it can include the dollar sign (
$
) and underscore character (
_
). No other special characters are allowed.
When picking a name for your method, try to pick a name that’s relatively short but descriptive. A method name such as
calculateTheTotalAmountOfTheInvoice
is a little long, but just
calc
is pretty ambiguous. Something along the lines of
calculateInvoiceTotal
seems more reasonable to me.
parameter list: You can pass one or more values to a method by listing the values in parentheses following the method name. The parameter list in the method declaration lets Java know what types of parameters a method should expect to receive and provides names so that the statements in the method’s body can access the parameters as local variables. You discover more about parameters in the section “Methods That Take Parameters ,” later in this chapter.
If the method doesn’t accept parameters, you must still code the parentheses that surround the parameter list. You just leave the parentheses empty.
if
,
while
, and
for
, the method body requires you to use the braces even if the body consists of only one statement.Okay, all that was a little abstract. Now, for a concrete example, I offer a version of the
Hello, World!
program in which the message is displayed not by the
main
method, but by a method named
sayHello
that’s called by the
main
method:
public class HelloWorldMethod
{
public static void main(String[] args)
{
sayHello();
}
public static void sayHello()
{
System.out.println("Hello, World!");
}
}
This program is admittedly trivial, but it illustrates the basics of creating and using methods in Java. Here, the statement in the
main
method calls the
sayHello
method, which in turn displays a message on the console.
public class HelloWorldMethod
{
public static void sayHello()
{
System.out.println("Hello, World!");
}
public static void main(String[] args)
{
sayHello();
}
}
This version of the program works exactly like the preceding version.
Okay, the last example was kind of dumb. No one in his (or her) right mind would create a method that has just one line of code and then call it from another method that also
has just one line of code. The
Hello, World!
program is too trivial to illustrate anything remotely realistic.
A program in Book 2, Chapter 5
, plays a guessing game. Most of this program’s
main
method is a large
while
loop that repeats the game as long as the user wants to keep playing. This loop has 41 statements in its body. That’s not so bad, but what if the game were 100 times more complicated, so that the
while
loop needed 4,100 statements to play a single cycle of the game? Do you really want a
while
loop that has 4,100 statements in its body? I should think not.
Listing 7-1
shows how you can simplify this game a bit just by placing the body of the main
while
loop in a separate method. I called this method
playARound
, because its job is to play one round of the guessing game. Now, instead of actually playing a round of the game, the
main
method of this program delegates that task to the
playARound
method.
LISTING 7-1 A Version of the Guessing-Game Program That Uses a playARound Method
import java.util.Scanner;
public class GuessingGameMethod
{
static Scanner sc = new Scanner(System.in);
static boolean keepPlaying = true; →7
public static void main(String[] args)
{
System.out.println("Let's play a guessing game!");
while (keepPlaying) →12
{
playARound(); →14
}
System.out.println("\nThank you for playing!");
}
public static void playARound() →19
{
boolean validInput;
int number, guess;
String answer;
// Pick a random number
number = (int)(Math.random() * 10) + 1;
System.out.println("\nI'm thinking of a number "
+ "between 1 and 10.");
// Get the guess
System.out.print("What do you think it is? ");
do
{
guess = sc.nextInt();
validInput = true;
if ((guess < 1) || (guess > 10))
{
System.out.print("I said, between 1 "
+ "and 10. Try again: ");
validInput = false;
}
} while (!validInput);
// Check the guess
if (guess == number)
System.out.println("You're right!");
else
System.out.println("You're wrong!"
+ " The number was " + number);
// Play again?
do
{
System.out.print("\nPlay again? (Y or N)");
answer = sc.next();
validInput = true;
if (answer.equalsIgnoreCase("Y"));
else if (answer.equalsIgnoreCase("N"))
keepPlaying = false; →60
else
validInput = false;
} while (!validInput);
}
}
Here are a few important details to notice about this method:
→7
Because both the
main
method (in line 12) and the
playARound
method (in line 60) must access the
keepPlaying
variable, I declare it as a class variable rather than as a local variable in the
main
method.
Class variables must be static if you intend to access them from static methods.
while
loop in the
main
method is just one line: a call to the
playARound
method. Thus, each time the loop repeats, the program plays one round of the game with the user.playARound
method marks the method as
static
so that the static
main
method can call it.Methods that just do work without returning any data are useful only in limited situations. The real utility of methods comes when they can perform some mundane task such as a calculation and then return the value of that calculation to the calling method so that the calling method can do something with the value. You find out how to do that in the following sections.
To create a method that returns a value, you simply indicate the type of the value returned by the method on the method declaration in place of the
void
keyword. Here’s a method declaration that creates a method that returns an
int
value:
public static int getRandomNumber()
Here the
getRandomNumber
method calculates a random number and then returns the number to the caller.
int
long
float
char
short
byte
double
boolean
Alternatively, the return type can be a reference type
, including a class defined by the API such as
String
or a class you create yourself.
When you specify a return type other than
void
in a method declaration, the body of the method must include a
return
statement that specifies the value to be returned. The
return
statement has this form:
return expression
;
The expression must evaluate to a value that’s the same type as the type listed in the method declaration. In other words, if the method returns an
int
, the expression in the
return
statement must evaluate to an
int
.
Here’s a program that uses a method that determines a random number between 1 and 10:
public class RandomNumber
{
public static void main(String[] args)
{
int number = getRandomNumber();
System.out.println("The number is " + number);
}
public static int getRandomNumber()
{
int num = (int)(Math.random() * 10) + 1;
return num;
}
}
In this program, the
getRandomNumber
method uses the
Math.random
method to calculate a random number from 1 to 10. (For more information about the
Math.random
method, see Book 2, Chapter 3
.) The
return
statement returns the random number that was calculated.
Because the
return
statement can specify an expression as well as a simple variable, I could just as easily have written the
getRandomNumber
method like this:
public static int getRandomNumber()
{
return (int)(Math.random() * 10) + 1;
}
Here the
return
statement includes the expression that calculates the random number.
You can use a method that returns a value in an assignment statement, like this:
int number = getRandomNumber();
Here the
getRandomNumber
method is called, and the value it returns is assigned to the variable
number
.
You can also use methods that return values in expressions — such as
number = getRandomNumber() * 10;
Here the value returned by the
getRandomNumber
method is multiplied by 10, and the result is assigned to
number
.
If a method declares a return type other than void, it must
use a
return
statement to return a value. The compiler doesn’t let you get away with a method that doesn’t have a correct
return
statement.
Things can get complicated if your
return
statements are inside
if
statements. Sometimes, the compiler gets fooled and refuses to compile your program. To explain this situation, I offer the following tale of multiple attempts to solve what should be a simple programming problem.
Suppose that you want to create a random-number method that returns random numbers between 1 and 20 but never returns 12 (because you have the condition known as dodecaphobia, which — as Lucy from Peanuts would tell you — is the fear of the number 12). Your first thought is to just ignore the 12s, like this:
public static int getRandomNumber()
{
int num = (int)(Math.random() * 20) + 1;
if (num != 12)
return num;
}
The compiler isn’t fooled by your trickery here, however. It knows that if the number is 12, the
return
statement won’t get executed, so it issues the message
missing return statement
and refuses to compile your program.
Your next thought is to simply substitute 11 whenever 12 comes up:
public static int getRandomNumber()
{
int num = (int)(Math.random() * 20) + 1;
if (num != 12)
return num;
else
return 11;
}
Later that day, you realize that this solution isn’t a good one because the number isn’t really random anymore. One of the requirements of a good random-number generator is that any number should be as likely as any other number to come up next. But because you’re changing all 12s to 11s, you’ve made 11 twice as likely to come up as any other number.
To fix this error, you decide to put the random-number generator in a loop that ends only when the random number is not 12:
public static int getRandomNumber()
{
int num;
do
{
num = (int)(Math.random() * 20) + 1;
if (num != 12)
return num;
} while (num == 12);
}
But the compiler refuses to compile the method again. It turns out that the compiler is smart, but not very smart. It doesn’t catch the fact that the condition in the
do-while
loop is the opposite of the condition in the
if
statement, meaning that the only way out of this loop is through the
return
statement in the
if
statement. So the compiler whines
missing return statement
again.
After thinking about it for a while, you come up with this solution:
public static int getRandomNumber()
{
int num;
while (true)
{
num = (int)(Math.random() * 20) + 1;
if (num != 12)
return num;
}
}
Now everyone’s happy. The compiler knows that the only way out of the loop is through the
return
statement, your dodecaphobic user doesn’t have to worry about seeing the number 12, and you know that the random number isn’t twice as likely to be 11 as any other number. Life is good, and you can move on to the next topic.
To illustrate the benefits of using methods that return values, Listing 7-2
presents another version of the guessing-game program that uses four methods in addition to
main
:
LISTING 7-2 Another Version of the Guessing-Game Program
import java.util.Scanner;
public class GuessingGameMethod2
{
static Scanner sc = new Scanner(System.in);
public static void main(String[] args)
{
System.out.println("Let's play a guessing game!");
do →11
{
playARound(); →13
} while (askForAnotherRound()); →14
System.out.println("\nThank you for playing!");
}
public static void playARound() →18
{
boolean validInput;
int number, guess;
String answer;
// Pick a random number
number = getRandomNumber(); →25
// Get the guess
System.out.println("\nI'm thinking of a number "
+ "between 1 and 10.");
System.out.print("What do you think it is? ");
guess = getGuess(); →31
// Check the guess
if (guess == number)
System.out.println("You're right!");
else
System.out.println("You're wrong!"
+ " The number was " + number);
}
public static int getRandomNumber() →41
{
return (int)(Math.random() * 10) + 1; →43
}
public static int getGuess() →46
{
while (true) →48
{
int guess = sc.nextInt();
if ((guess < 1) || (guess > 10))
{
System.out.print("I said, between 1 and 10. "
+ "Try again: ");
}
else
return guess; →57
}
}
public static boolean askForAnotherRound() →61
{
while (true) →63
{
String answer;
System.out.print("\nPlay again? (Y or N) ");
answer = sc.next();
if (answer.equalsIgnoreCase("Y"))
return true; →69
else if (answer.equalsIgnoreCase("N"))
return false; →71
}
}
}
The following paragraphs point out the key lines of this program:
do
loop in the
main
method. Each cycle of this
do
loop plays one round of the game. The
do
loop continues until the user indicates that he or she wants to stop playing.playARound
method to play one round of the game.askForAnotherRound
method to determine whether the user wants to play another round. The
boolean
return value from this method is used as the expression for the
do
loop. Thus, the
do
loop repeats if the
askForAnotherRound
method returns
true
.playARound
method.getRandomNumber
method to get a random number between 1 and 10. The value returned by this method is stored in the
number
variable.getGuess
method to get the user’s guess. This method returns a number between 1 and 10, which is stored in the
guess
variable.getRandomNumber
method, which indicates that this method returns an
int
value.return
statement for the
getRandomNumber
method. The random number is calculated using the
Math.random
method, and the result of this calculation is returned as the value of the
getRandomNumber
method.getGuess
method, which indicates that this method returns an
int
value.getGuess
method uses a
while
loop, which exits only when the user enters a number between 1 and 10.return
statement for the
getGuess
method. Note that this
return
statement is in the
else
part of an
if
statement that checks whether the number is less than 1 or greater than 10. If the number is outside the acceptable range, the
return
statement isn’t executed. Instead, the program displays an error message, and the
while
loop repeats.askForAnotherRound
method, which returns a
boolean
value.askForAnotherRound
method, which uses a
while
loop that exits only when the user enters a valid
Y
or
N
response.askForAnotherRound
method, which returns
true
if the user enters
Y
or
y
.askForAnotherRound
method, which returns
false
if the user enters
N
or
n
.A parameter is a value that you can pass to a method. Then the method can use the parameter as though it were a local variable initialized with the value of the variable passed to it by the calling method.
The guessing-game application shown in Listing 7-2
has a method named
getRandomNumber
that returns a random number between 1 and 10:
public static int getRandomNumber()
{
return (int)(Math.random() * 10) + 1;
}
This method is useful, but it would be even more useful if you could tell it the range of numbers you want the random number to fall in. It would be nice to call the method like this to get a random number between 1 and 10:
int number = getRandomNumber(1, 10);
Then, if your program needs to roll dice, you could call the same method:
int number = getRandomNumber(1, 6);
Or, to pick a random card from a deck of 52 cards, you could call it like this:
int number = getRandomNumber(1, 52);
You wouldn’t have to start with 1, either. To get a random number between 50 and 100, you’d call the method like this:
int number = getRandomNumber(50, 100);
In the following sections, you write methods that accept parameters.
A method that accepts parameters must list the parameters in the method declaration. The parameters are placed in a parameter list inside the parentheses that follow the method name. For each parameter used by the method, you list the parameter type followed by the parameter name. If you need more than one parameter, you separate the parameters with commas.
Here’s a version of the
getRandomNumber
method that accepts parameters:
public static int getRandomNumber(int min, int max)
{
return (int)(Math.random()
* (max - min + 1)) + min;
}
Here the method uses two parameters, both of type
int
, named
min
and
max
. Then, within the body of the method, these parameters can be used as though they were local variables.
int min = 1;
int max = 10;
int number = getRandomNumber(min, max);
Or you could call it like this:
int low = 1;
int high = 10;
int number = getRandomNumber(low, high);
Or you could dispense with the variables altogether and just pass literal values to the method:
int number = getRandomNumber(1, 10);
You can also specify expressions as the parameter values:
int min = 1;
int max = 10;
int number = getRandomNumber(min * 10, max * 10);
Here
number
is assigned a value between 10 and 100.
The scope of a parameter is the method for which the parameter is declared. As a result, a parameter can have the same name as local variables used in other methods without causing any conflict. Consider this program:
public class ParameterScope
{
public static void main(String[] args)
{
int min = 1;
int max = 10;
int number = getRandomNumber(min, max);
System.out.println(number);
}
public static int getRandomNumber(int min, int max)
{
return (int)(Math.random()
* (max - min + 1)) + min;
}
}
Here the
main
method declares variables named
min
and
max
, and the
getRandomNumber
method uses
min
and
max
for its parameter names. This doesn’t cause any conflict, because in each case the scope is limited to a single method.
When Java passes a variable to a method via a parameter, the method itself receives a copy of the variable’s value, not the variable itself. This copy is called a pass-by-value, and it has an important consequence: If a method changes the value it receives as a parameter, that change is not reflected in the original variable that was passed to the method. The following program can help clear this up:
public class ChangeParameters
{
public static void main(String[] args)
{
int number = 1;
tryToChangeNumber(number);
System.out.println(number);
}
public static void tryToChangeNumber(int i)
{
i = 2;
}
}
Here a variable named
number
is set to
1
and then passed to the method named
tryToChangeNumber
. This method receives the variable as a parameter named
i
and then sets the value of
i
to
2
. Meanwhile, back in the
main
method,
println
is used to print the value of
number
after the
tryToChangeNumber
method returns.
Because
tryToChangeNumber
gets only a copy of
number
, not the
number
variable itself, this program displays the following on the console (drum roll, please …):
1
.
The key point is this: Even though the
tryToChangeNumber
method changes the value of its parameter, that change has no effect on the original variable that was passed to the method.
To show off the benefits of methods that accept parameters, Listing 7-3
shows one more version of the guessing-game program. This version uses the following methods in addition to
main
:
playARound
:
This method plays one round of the guessing game. It doesn’t return a value, but it accepts two arguments,
min
and
max
, that indicate the minimum and maximum values for the number to be guessed.getRandomNumber
:
This method returns a random number between
min
and
max
values passed as parameters.getGuess
:
This method also accepts two parameters,
min
and
max
, to limit the range within which the user must guess.askForAnotherRound
:
This method asks the user to play another round and returns a
boolean
value to indicate whether the user wants to continue playing. It accepts a
String
value as a parameter; this string is displayed on the console to prompt the user for a reply.LISTING 7-3 Yet Another Version of the Guessing-Game Program
import java.util.Scanner;
public class GuessingGameMethod3
{
static Scanner sc = new Scanner(System.in);
public static void main(String[] args)
{
System.out.println("Let's play a guessing game!");
do
{
playARound(1, getRandomNumber(7, 12)); →13
} while (askForAnotherRound("Try again?"));
System.out.println("\nThank you for playing!");
}
public static void playARound(int min, int max)
{
boolean validInput;
int number, guess;
String answer;
// Pick a random number
number = getRandomNumber(min, max); →25
// Get the guess
System.out.println("\nI'm thinking of a number "
+ "between " + min + " and " + max + "."); →29
System.out.print("What do you think it is? ");
guess = getGuess(min, max); →31
// Check the guess
if (guess == number)
System.out.println("You're right!");
else
System.out.println("You're wrong!"
+ " The number was " + number);
}
public static int getRandomNumber(int min, int max) →41
{
return (int)(Math.random() →43
* (max - min + 1)) + min;
}
public static int getGuess(int min, int max) →47
{
while (true)
{
int guess = sc.nextInt();
if ( (guess < min) || (guess > max) ) →52
{
System.out.print("I said, between "
+ min + " and " + max
+ ". Try again: ");
}
else
return guess; →59
}
}
public static boolean askForAnotherRound(String prompt) →63
{
while (true)
{
String answer;
System.out.print("\n" + prompt + " (Y or N) ");
answer = sc.next();
if (answer.equalsIgnoreCase("Y"))
return true;
else if (answer.equalsIgnoreCase("N"))
return false;
}
}
}
The following paragraphs point out the key lines of this program:
playARound
method to play one round of the game. The values for
min
and
max
are passed as literals. To add a small amount of variety to the game, the
getRandomNumber
method is called here to set the value for the
max
to a random number from 7 to 12.getRandomNumber
method passes the values of
min
and
max
as parameters to set the range for the random numbers.min
and
max
parameters to indicate the range.getGuess
method now passes the range of acceptable guesses to the
getGuess
method.getRandomNumber
method specifies the
min
and
max
parameters.min
may not be
1
.getGuess
method accepts the
min
and
max
parameters.if
statement in the
getGuess
method uses the
min
and
max
values to validate the user’s input.return
statement for the
getGuess
method. Note that this
return
statement is in the
else
part of an
if
statement that checks whether the number is less than 1 or greater than 10. If the number is outside the acceptable range, the
return
statement isn’t executed. Instead, the program displays an error message, and the
while
loop repeats.askForAnotherRound
method accepts a string variable to use as a prompt.