Adding Some Methods to Your Madness

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 with using methods. For example, to print text to the console, you use the println or print method. To get an integer from the user, you use the nextInt method. And to compare string values, you use either the equals method or the equalsIgnoreCase method. And the granddaddy of all methods, main, is the method that 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 methods that are defined by the Java API and that belong to a particular Java class. For example, the nextInt method belongs to the Scanner class, and the equalsIgnoreCase method belongs to the String class.

In 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 a part of your application's class. You can then call these methods from your main method. As you'll see, this method turns out to be very useful for all but the shortest Java programs.

The Joy of Methods

The use of methods can dramatically improve the quality of your programming life. For example, suppose 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 up 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 your program needs to perform some calculation, such as how long to let the main rockets burn to make a mid-course correction on a moon flight, and the program needs to perform this calculation in several different places. Without methods, you'd have to duplicate the statements that do this calculation. That's not only error-prone, but 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. For example, suppose you have a while loop that has 500 statements in its body. That makes it pretty hard to track down the closing 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.

  TECHNICAL STAUFF 

At this point, the object-oriented programming zealots in the audience are starting to boo and hiss. A few of them have already left the auditorium. They're upset because I'm describing methods in traditional procedural-programming terms instead of modern object-oriented programming terms.

Well, phooey. They're right, but so what? I get to the object-oriented uses for methods in Book III. There, you find out that methods have a far greater purpose than simply breaking a long main method into smaller pieces. But even so, some of the most object-oriented programs I know use methods just to avoid repetitive code or to slice a large method into a couple of smaller ones. So there.

The Basics of Making Methods

All methods-including the main method-must begin with a method declaration. Here's the basic form for 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 method declarations piece by piece:

An example

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.

  REMEMBER 

The order in which methods appear in your Java source file doesn't matter. The only rule is that all the methods must be declared within the body of the class-that is, between the first left brace and the last right brace. For example, here's a version of the HelloWorldMethod program in which I reversed the order of the methods:

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 previous version.

Another example

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.

For example, a program in Book II, 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 into 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

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 quessing game"); while (keepPlaying) → 12 { playARound(); → 14 } System.out.println(" Thank 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(" I'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(" Play again? (Y or N)"); answer = sc.next(); validInput = true; if (answer.equalsIgnoreCase("Y")) ; else if (answer.equalsIgnoreCase("N")) keepPlaying = false; else validInput = false; } while (!validInput); } }

Here are a few important details to notice about this method:

7

Because the main method (in line 12) and the playARound method (in line 60) must both access the keepPlaying variable, I declared it as a class variable rather than as a local variable in the main method.

  REMEMBER 

Class variables must be static if you intend to access them from static methods.

Open table as spreadsheet

14

The body of the 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.

19

The declaration for the playARound method marks the method as static so that the static main method can call it.

  Tip 

The body of the playARound method is identical to the body of the while loop that was originally used in the single-method version of this program shown in Book II, Chapter 5. If you want a refresher on how this code works, I politely refer you to that chapter.

Methods That Return Values

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 the calling method can do something with the value. You find out how to do that in the following sections.

Declaring the method s return type

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. For example, 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.

  REMEMBER 

The return type of a method can be any of Java's primitive return types (described in Book II, Chapter 2):

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.

Using the return statement to return the value

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.

For example, 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 II, 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.

Using a method that returns a type

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.

You gotta have a proper return statement

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 sometimes get complicated if your return statements are inside if statements. Sometimes, the compiler can get fooled and refuse to compile your program. To explain this, I offer the following tale of multiple attempts to solve what should be a simple programming problem:

Suppose 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 duodecaphobia, 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; }

However, the compiler isn't fooled by your trickery here. 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; }

However, later that day you realize 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 now the compiler refuses to compile the method again. It turns out that the compiler is smart, but not real 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 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 the only way out of the loop is through the return statement, your doudecaphobic 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.

Another version of the Guessing Game program

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 quessing game"); do → 11 { playARound(); → 13 } while (askForAnotherRound()); → 14 System.out.println(" Thank 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(" I'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(" Play 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:

11

The start of the 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.

13

Calls the playARound method to play one round of the game.

14

Calls the 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.

18

The start of the playARound method.

25

This line calls the getRandomNumber method to get a random number between 1 and 10. The value returned by this method is stored in the number variable.

31

This line calls the getGuess method to get the user's guess.

This method returns a number between 1 and 10, which is stored in the guess variable.

41

The start of the getRandomNumber method, which indicates that this method returns an int value.

43

The 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.

46

The start of the getGuess method, which indicates that this method returns an int value.

48

The getGuess method uses a while loop, which exits only when the user enters a number between 1 and 10.

57

The return statement for the getGuess method. Note that this return statement is in the else part of an if statement that checks if the number is less than 1 or greater than 10. If the number is outside of the acceptable range, the return statement isn't executed. Instead, the program displays an error message, and the while loop repeats.

61

The start of the askForAnotherRound method, which returns a boolean value.

63

The askForAnotherRound method uses a while loop that exits only when the user enters a valid Y or N response.

69

The askForAnotherRound method returns true if the user enters Y or y.

71

The askForAnotherRound method returns false if the user enters N or n.

Open table as spreadsheet

Using Methods That Take Parameters

A parameter is a value that you can pass to a method. The method can then use the parameter as if it were a local variable initialized with the value of the variable passed to it by the calling method.

For example, the guessing game application that was 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. For example, it would be nice if you could call it 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);

And you wouldn't have to start with 1, either. To get a random number between 50 and 100, you'd call it like this:

int number = getRandomNumber(50, 100);

In the following sections, you write methods that accept parameters.

Declaring parameters

A method that accepts parameters must list the parameters in the method declaration. The parameters are listed in a parameter list that's in 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 them with commas.

For example, 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 if they were local variables.

  Tip 

The names you use for parameters can be the same as the names you use for the variables you pass to the method when you call it, but they don't have to be. For example, you could call the getRandomNumber method like this:

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 can 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.

Scoping out parameters

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. For example, 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.

Understanding pass by value

  TECHNICAL STAUFF 

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 only gets a copy of number and not the number variable itself, this program displays the following on the console (drumroll 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.

Yet another example of the Guessing Game program

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:

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(" Thank 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(" I'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(" " + 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:

13

Calls the 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.

25

The call to the getRandomNumber method passes the values of min and max as parameters to set the range for the random numbers.

29

The message that announces to the user that the computer has chosen a random number uses the min and max parameters to indicate the range.

31

The call to the getGuess method now passes the range of acceptable guesses to the getGuess method.

41

The declaration for the getRandomNumber method specifies the min and max parameters.

43

The calculation for the random number is complicated a bit by the fact that min might not be 1.

47

The declaration for the getGuess method accepts the min and max parameters.

52

The if statement in the getGuess method uses the min and max values to validate the user's input.

59

The return statement for the getGuess method. Note that this return statement is in the else part of an if statement that checks if the number is less than 1 or greater than 10. If the number is outside of the acceptable range, the return statement isn't executed. Instead, the program displays an error message, and the while loop repeats.

63

The askForAnotherRound method accepts a string variable to use as a prompt.

Open table as spreadsheet

Категории