Chapter 19
IN THIS CHAPTER
Writing methods that work with existing values
Building methods that modify existing values
Making methods that return new values
In Chapters 3 and 4, I introduce Java methods. I show you how to create a main
method and how to call the System.out.println
method. Between that chapter and this one, I make very little noise about methods. In Chapter 18, I introduce a bunch of new methods for you to call, but that’s only half of the story.
This chapter completes the circle. In this chapter, you create your own Java methods — not the tired old main
method that you’ve been using all along, but rather some new, powerful Java methods.
In Chapter 18, Figure 18-6 introduces an interesting notion — a notion that’s at the core of object-oriented programming. Each Java string has its own equals
method. That is, each string has, built within it, the functionality to compare itself to other strings. That’s an important point. When you do object-oriented programming, you bundle data and functionality into a lump called a class. Just remember Barry’s immortal words from Chapter 17:
A class … describes the way in which you intend to combine and use pieces of data.
And why are these words so important? They’re important because, in object-oriented programming, chunks of data take responsibility for themselves. With object-oriented programming, everything you have to know about a string is located in the file String.java
. So, if people have problems with the strings, they know just where to look for all the code. That’s great!
This is the deal: Objects contain methods. Chapter 18 shows you how to use an object’s methods, and this chapter shows you how to create an object’s methods.
Imagine a table containing the information about three accounts. (If you have trouble imagining such a thing, just look at Figure 19-1.) In the figure, each account has a last name, an identification number, and a balance. In addition (and here’s the important part), each account knows how to display itself on the screen. Each row of the table has its own copy of a display
method.
The last names in Figure 19-1 may seem strange to you. That’s because I generated the table’s data randomly. Each last name is a haphazard combination of three letters: one uppercase letter followed by two lowercase letters.
To find out how I randomly generate three-letter names, see this chapter’s “Generating words randomly” sidebar.
I need some code to implement the ideas in Figure 19-1. Fortunately, I have some code in Listing 19-1.
LISTING 19-1 An Account Class
import java.text.NumberFormat;
import static java.lang.System.out;
class Account {
String lastName;
int id;
double balance;
void display() {
NumberFormat currency = NumberFormat.getCurrencyInstance();
out.print("The account with last name ");
out.print(lastName);
out.print(" and ID number ");
out.print(id);
out.print(" has balance ");
out.println(currency.format(balance));
}
}
The Account
class in Listing 19-1 defines four members: a lastName
field, an id
field, a balance
field, and a display
method. So each instance of Account
class has its own lastName
, its own id
, its own balance
, and its own way of doing display
. These things match up with the four columns in Figure 19-1.
Listing 19-1 contains the display
method’s declaration. Like a main
method’s declaration, the display
declaration has a header and a body. (See Chapter 4.) The header has two words and some parentheses:
The word void
tells the computer that, when the display
method is called, the display
method doesn’t return anything to the place that called it.
Later in this chapter, a method does return something. For now, the display
method returns nothing.
The word display
is the method’s name.
Every method must have a name. Otherwise, you don’t have a way to call the method.
The parentheses contain all the things you’re going to pass to the method when you call it.
When you call a method, you can pass information to that method on the fly. This display
example, with its empty parentheses, looks strange. That’s because no information is passed to the display
method when you call it. That’s okay. I give a meatier example later in this chapter.
The display
method’s body contains some print
and println
calls. The interesting thing here is that the body makes reference to the lastName
, id
, and balance
fields. A method’s body can do that. But with each object having its own lastName
, id
, and balance
variables, what does a variable in the display
method’s body mean?
Well, when I use the Account
class, I create little account objects. Maybe I create an object for each row of the table in Figure 19-1. Each object has its own values for the lastName
, id
, and balance
variables, and each object has its own copy of the display
method.
Take the first display
method in Figure 19-1 — the method for Aju’s account. The display
method for that object behaves as though it had the code in Listing 19-2.
LISTING 19-2 How the display Method Behaves When No One’s Looking
/*
* This is not real code:
*/
void display() {
NumberFormat currency = NumberFormat.getCurrencyInstance();
out.print("The account with last name ");
out.print("Aju");
out.print(" and ID number ");
out.print(9936);
out.print(" has balance ");
out.println(currency.format(8734.00));
}
In fact, each of the three display
methods behaves as though its body has a slightly different code. Figure 19-2 illustrates this idea for two instances of the Account
class.
To put the previous section’s ideas into action, you need more code. So the next listing (see Listing 19-3) creates instances of the Account
class.
LISTING 19-3 Making Use of the Code in Listing 19-1
import java.util.Random;
class ProcessAccounts {
public static void main(String args[]) {
Random myRandom = new Random();
Account anAccount;
for (int i = 0; i < 3; i++) {
anAccount = new Account();
anAccount.lastName = "" +
(char) (myRandom.nextInt(26) + ’A’) +
(char) (myRandom.nextInt(26) + ’a’) +
(char) (myRandom.nextInt(26) + ’a’);
anAccount.id = myRandom.nextInt(10000);
anAccount.balance = myRandom.nextInt(10000);
anAccount.display();
}
}
}
Here’s a summary of the action in Listing 19-3:
Do the following three times:
Create a new object (an instance of the Account class).
Randomly generate values for the object’s lastName, id and balance.
Call the object’s display method.
The first of the three display
calls prints the first object’s lastName
, id
, and balance
values. The second display
call prints the second object’s lastName
, id
, and balance
values. And so on.
A run of the code from Listing 19-3 is shown in Figure 19-3.
Suppose that you’re running the code in Listing 19-3. The computer reaches the display
method call:
anAccount.display();
At that point, the computer starts running the code inside the display
method. In other words, the computer jumps to the middle of the Account
class’s code (the code in Listing 19-1).
After executing the display
method’s code (that forest of print
and println
calls), the computer returns to the point where it departed from in Listing 19-3. That is, the computer goes back to the display
method call and continues on from there.
When you run the code in Listing 19-3, the flow of action in each loop iteration isn’t exactly from the top to the bottom. Instead, the action goes from the for
loop to the display
method and then back to the for
loop. The whole business is pictured in Figure 19-4.
In Listing 19-3, notice the use of dots. To refer to the lastName
stored in the anAccount
object, you write
anAccount.lastName
To get the anAccount
object to display
itself, you write
anAccount.display();
That’s great! When you refer to an object’s field or call an object’s method, the only difference is parentheses:
The program in Listing 19-3 uses some cute tricks. In Java, you can do two different things with a plus sign:
You can add numbers with a plus sign.
For example, you can write
numberOfSheep = 2 + 5;
You can concatenate strings with a plus sign.
When you concatenate strings, you scrunch them together, one right after another. For example, the expression
"Barry" + " " + "Burd"
scrunches together Barry
, a blank space, and Burd
. The new scrunched-up string is (you guessed it) Barry Burd
.
In Listing 19-3, the statement
anAccount.lastName = "" +
(char) (myRandom.nextInt(26) + ’A’) +
(char) (myRandom.nextInt(26) + ’a’) +
(char) (myRandom.nextInt(26) + ’a’);
has many plus signs, and some of the plus signs concatenate things together. The first thing is a mysterious empty string (""
). This empty string is invisible, so it never gets in the way of your seeing the second, third, and fourth things.
Onto the empty string, the program concatenates a second thing. This second thing is the value of the expression (char) (myRandom.nextInt(26) + ’A’)
. The expression may look complicated, but it’s really no big deal. This expression represents an uppercase letter (any uppercase letter, generated randomly).
Onto the empty string and the uppercase letter, the program concatenates a third thing. This third thing is the value of the expression (char) (myRandom.nextInt(26) + ’a’)
. This expression represents a lowercase letter (any lowercase letter, generated randomly.)
Onto all this stuff, the program concatenates another lowercase letter. So altogether, you have a randomly generated three-letter name. For more details, see the upcoming sidebar.
When I was a young object, I wasn’t as smart as the objects you have nowadays. Consider, for example, the object in Listing 19-4. This object not only displays itself, but it can also fill itself with values.
LISTING 19-4 A Class with Two Methods
import java.util.Random;
import java.text.NumberFormat;
import static java.lang.System.out;
class BetterAccount {
String lastName;
int id;
double balance;
void fillWithData() {
Random myRandom = new Random();
lastName = "" +
(char) (myRandom.nextInt(26) + ’A’) +
(char) (myRandom.nextInt(26) + ’a’) +
(char) (myRandom.nextInt(26) + ’a’);
id = myRandom.nextInt(10000);
balance = myRandom.nextInt(10000);
}
void display() {
NumberFormat currency = NumberFormat.getCurrencyInstance();
out.print("The account with last name ");
out.print(lastName);
out.print(" and ID number ");
out.print(id);
out.print(" has balance ");
out.println(currency.format(balance));
}
}
I wrote some code to use the class in Listing 19-4. This new code is in Listing 19-5.
LISTING 19-5 This Is So Cool!
class ProcessBetterAccounts {
public static void main(String args[]) {
BetterAccount anAccount;
for (int i = 0; i < 3; i++) {
anAccount = new BetterAccount();
anAccount.fillWithData();
anAccount.display();
}
}
}
Listing 19-5 is pretty slick. Because the code in Listing 19-4 is so darn smart, the new code in Listing 19-5 has very little work to do. This new code just creates a BetterAccount
object and then calls the methods in Listing 19-4. When you run all this stuff, you get results like the ones in Figure 19-3.
Think about sending someone to the supermarket to buy bread. When you do this, you say, “Go to the supermarket and buy some bread.” (Try it at home. You’ll have a fresh loaf of bread in no time at all!) Of course, some other time, you send that same person to the supermarket to buy bananas. You say, “Go to the supermarket and buy some bananas.” And what’s the point of all of this? Well, you have a method, and you have some on-the-fly information that you pass to the method when you call it. The method is named “Go to the supermarket and buy some …” The on-the-fly information is either “bread” or “bananas,” depending on your culinary needs. In Java, the method calls would look like this:
goToTheSupermarketAndBuySome(bread);
goToTheSupermarketAndBuySome(bananas);
The things in parentheses are called parameters or parameter lists. With parameters, your methods become much more versatile. Rather than get the same thing each time, you can send somebody to the supermarket to buy bread one time, bananas another time, and birdseed the third time. When you call your goToTheSupermarketAndBuySome
method, you decide right there and then what you’re going to ask your pal to buy.
These concepts are made more concrete in Listings 19-6 and 19-7.
LISTING 19-6 Adding Interest
import java.text.NumberFormat;
import static java.lang.System.out;
class NiceAccount {
String lastName;
int id;
double balance;
void addInterest(double rate) {
out.print("Adding ");
out.print(rate);
out.println(" percent…");
balance += balance * (rate / 100.0);
}
void display() {
NumberFormat currency = NumberFormat.getCurrencyInstance();
out.print("The account with last name ");
out.print(lastName);
out.print(" and ID number ");
out.print(id);
out.print(" has balance ");
out.println(currency.format(balance));
}
}
LISTING 19-7 Calling the addInterest Method
import java.util.Random;
class ProcessNiceAccounts {
public static void main(String args[]) {
Random myRandom = new Random();
NiceAccount anAccount;
double interestRate;
for (int i = 0; i < 3; i++) {
anAccount = new NiceAccount();
anAccount.lastName = "" +
(char) (myRandom.nextInt(26) + ’A’) +
(char) (myRandom.nextInt(26) + ’a’) +
(char) (myRandom.nextInt(26) + ’a’);
anAccount.id = myRandom.nextInt(10000);
anAccount.balance = myRandom.nextInt(10000);
anAccount.display();
interestRate = myRandom.nextInt(5);
anAccount.addInterest(interestRate);
anAccount.display();
System.out.println();
}
}
}
In Listing 19-7, the line
anAccount.addInterest(interestRate);
plays the same role as the line goToTheSupermarketAndBuySome(bread)
in my little supermarket example. The word addInterest
is a method name, and the word interestRate
in parentheses is a parameter. Taken as a whole, this statement tells the code in Listing 19-6 to execute its addInterest
method. This statement also tells Listing 19-6 to use a certain number (whatever value is stored in the interestRate
variable) in the method’s calculations. The value of interestRate
can be 1.0, 2.0, or whatever other value you get by calling myRandom.nextInt(5)
. In the same way, the goToTheSupermarketAndBuySome
method works for bread, bananas, or whatever else you need from the market.
The next section has a detailed description of addInterest
and its action. In the meantime, a run of the code in Listings 19-6 and 19-7 is shown in Figure 19-5.
When you call a method, you can pass information to that method on the fly. This information is in the method’s parameter list. Listing 19-7 has a call to the addInterest
method:
anAccount.addInterest(interestRate);
The first time through the loop, the value of interestRate
is 2.0
. (Remember, I’m using the data in Figure 19-5.) At that point in the program’s run, the method call behaves as though it’s the following statement:
anAccount.addInterest(2.0);
The computer is about to run the code inside the addInterest
method (a method in Listing 19-6). But first, the computer passes the value 2.0
to the parameter in the addInterest
method’s header. Inside the addInterest
method, the value of rate
becomes 2.0
. For an illustration of this idea, see Figure 19-6.
Here’s something interesting. The parameter in the addInterest
method’s header is rate
. But, inside the ProcessNiceAccounts
class, the parameter in the method call is interestRate
. That’s okay. In fact, it’s standard practice.
In Listings 19-6 and 19-7, the names of the parameters don’t have to be the same. The only thing that matters is that both parameters (rate
and interestRate
) have the same type. In Listings 19-6 and 19-7, both of these parameters are of type double
. So everything is fine.
Inside the addInterest
method, the +=
assignment operator adds balance * (rate / 100.0)
to the existing balance
value. For some info about the +=
assignment operator, see Chapter 7.
In the next few bullets, I make some observations about the addInterest
method header (in Listing 19-6):
The word void
tells the computer that when the addInterest
method is called, the addInterest
method doesn’t send a value back to the place that called it.
The next section has an example in which a method sends a value back.
The word addInterest
is the method’s name.
That’s the name you use to call the method when you’re writing the code for the ProcessNiceAccounts
class. (See Listing 19-7.)
The parentheses in the header contain placeholders for all the things you’re going to pass to the method when you call it.
When you call a method, you can pass information to that method on the fly. This information is the method’s parameter list. The addInterest
method’s header says that the addInterest
method takes one piece of information, and that piece of information must be of type double
:
void addInterest(double rate)
Sure enough, if you look at the call to addInterest
(down in the ProcessNiceAccounts
class’s main
method), that call has the variable interestRate
in it. And interestRate
is of type double
. When I call addInterest
, I’m giving the method a value of type double
.
The addInterest
method in Listing 19-6 is called three times from the main
method in Listing 19-7. The actual account balances and interest rates are different each time:
In the first call of Figure 19-5, the balance is 8983.00, and the interest rate is 2.0.
When this call is made, the expression balance * (rate / 100.0)
stands for 8983.00 * (2.0 / 100.00). See Figure 19-7.
In the second call of Figure 19-5, the balance is 3756.00, and the interest rate is 0.0.
When the call is made, the expression balance * (rate / 100.0)
stands for 3756.00 * (0.0 / 100.00). Again, see Figure 19-7.
In the third call of Figure 19-5, the balance is 8474.00, and the interest rate is 3.0.
When the addInterest
call is made, the expression balance * (rate / 100.0)
stands for 8474.00 * (3.0 / 100.00).
Take a look at Listings 19-6 and 19-7. In those listings, the display
method has no parameters and the addInterest
method has one parameter. Now consider the following code from Chapter 11:
char letterGrade;
letterGrade = keyboard.findWithinHorizon(".",0).charAt(0);
In that code, the findWithinHorizon
method call has two parameters: the String
parameter "."
and the int
parameter 0
. That’s not unusual. You can create methods with as many parameters as you like. The only restriction is this: When you call a method, the types of the parameters in the call must match up with the types of parameters in the method declaration’s header. For example, in Java’s API code, the first line of the findWithinHorizon
method looks like this:
public String findWithinHorizon(String pattern, int horizon) {
And, in the method call findWithinHorizon(".",0)
, the first parameter "."
is a String
, and the second parameter 0
is an int
.
Listings 19-8 and 19-9 are variations on the code in Listings 19-6 and 19-7. In the new listings, the addInterest
method has two parameters: one for the interest rate and another for a number of years. When you call the addInterest
method, the method repeatedly adds interest for the number of years that you’ve specified.
A run of the code in Listings 19-8 and 19-9 is shown in Figure 19-8.
LISTING 19-8 Adding Interest for a Certain Number of Years
import java.text.NumberFormat;
import static java.lang.System.out;
class NiceAccount {
String lastName;
int id;
double balance;
void addInterest(double rate, int howManyYears) {
for (int i = 1; i <= howManyYears; i++) {
out.print("Adding ");
out.print(rate);
out.println(" percent…");
balance += balance * (rate / 100.0);
}
}
void display() {
NumberFormat currency = NumberFormat.getCurrencyInstance();
out.print("The account with last name ");
out.print(lastName);
out.print(" and ID number ");
out.print(id);
out.print(" has balance ");
out.println(currency.format(balance));
}
}
LISTING 19-9 Calling the Beefed-Up addInterest Method
import java.util.Random;
class ProcessNiceAccounts {
public static void main(String args[]) {
Random myRandom = new Random();
NiceAccount anAccount;
double interestRate;
for (int i = 0; i < 3; i++) {
anAccount = new NiceAccount();
anAccount.lastName = "" +
(char) (myRandom.nextInt(26) + ’A’) +
(char) (myRandom.nextInt(26) + ’a’) +
(char) (myRandom.nextInt(26) + ’a’);
anAccount.id = myRandom.nextInt(10000);
anAccount.balance = myRandom.nextInt(10000);
anAccount.display();
interestRate = myRandom.nextInt(5);
anAccount.addInterest(interestRate, 3);
anAccount.display();
System.out.println();
}
}
}
Say that you’re sending a friend to buy groceries. You make requests for groceries in the form of method calls. You issue calls such as
goToTheSupermarketAndBuySome(bread);
goToTheSupermarketAndBuySome(bananas);
The things in parentheses are parameters. Each time you call your goToTheSupermarketAndBuySome
method, you put a different value in the method’s parameter list.
Now what happens when your friend returns from the supermarket? “Here’s the bread you asked me to buy,” says your friend. As a result of carrying out your wishes, your friend returns something to you. You made a method call, and the method returns information (or better yet, the method returns some food).
The thing returned to you is called the method’s return value, and the type of thing returned to you is called the method’s return type.
To see how return values and a return types work in a real Java program, check out the code in Listings 19-10 and 19-11.
LISTING 19-10 A Method That Returns a Value
import java.text.NumberFormat;
import static java.lang.System.out;
class GoodAccount {
String lastName;
int id;
double balance;
double getInterest(double rate) {
double interest;
out.print("Adding ");
out.print(rate);
out.println(" percent…");
interest = balance * (rate / 100.0);
return interest;
}
void display() {
NumberFormat currency = NumberFormat.getCurrencyInstance();
out.print("The account with last name ");
out.print(lastName);
out.print(" and ID number ");
out.print(id);
out.print(" has balance ");
out.println(currency.format(balance));
}
}
LISTING 19-11 Calling the Method in Listing 19-10
import java.util.Random;
import java.text.NumberFormat;
class ProcessGoodAccounts {
public static void main(String args[]) {
Random myRandom = new Random();
NumberFormat currency = NumberFormat.getCurrencyInstance();
GoodAccount anAccount;
double interestRate;
double yearlyInterest;
for (int i = 0; i < 3; i++) {
anAccount = new GoodAccount();
anAccount.lastName = "" +
(char) (myRandom.nextInt(26) + ’A’) +
(char) (myRandom.nextInt(26) + ’a’) +
(char) (myRandom.nextInt(26) + ’a’);
anAccount.id = myRandom.nextInt(10000);
anAccount.balance = myRandom.nextInt(10000);
anAccount.display();
interestRate = myRandom.nextInt(5);
yearlyInterest = anAccount.getInterest(interestRate);
System.out.print("This year’s interest is ");
System.out.println(currency.format(yearlyInterest));
System.out.println();
}
}
}
To see a run of code from Listings 19-10 and 19-11, take a look at Figure 19-9.
I want to trace a piece of the action in Listings 19-10 and 19-11. For input data, I use the first set of values in Figure 19-9.
Here’s what happens when getInterest
is called (you can follow along in Figure 19-10):
balance
is 9508.00
, and the value of rate
is 2.0
. So the value of balance * (rate / 100.0)
is 190.16
— one hundred ninety dollars and sixteen cents.190.16
gets assigned to the interest
variable, so the statement
return interest;
has the same effect as
return 190.16;
return
statement sends this value 190.16
back to the code that called the method. At that point in the process, the entire method call in Listing19-11 — anAccount.getInterest(interestRate)
— takes on the value 190.16
.190.16
gets assigned to the variable yearlyInterest
.When you create a method or a method call, you have to be careful to use Java’s types consistently. Make sure that you check for the following:
getInterest
method’s header starts with the word double
. When the method is executed, it should send a double
value back to the place that called it.getInterest
method is return interest
. The method returns whatever value is stored in the interest
variable, and the interest
variable has type double
. So far, so good.getInterest
is assigned to a variable named yearlyInterest
. Sure enough, yearlyInterest
is of type double
.That settles it! The use of types in the handling of method getInterest
is consistent in Listings 19-10 and 19-11. I’m thrilled!
In the code that follows, replace the comments with statements that do what the comments suggest:
class Counter {
int count = 0;
void increment() {
// Add 1 to the value of count
}
}
public class Main {
public static void main(String[] args) {
Counter counter = new Counter();
// Call the counter object’s increment method
System.out.println(counter.count);
}
}
Modify the code in the previous “One, two, three” paragraph so that
increment
method has a parameter, andmain
method calls increment
several times, each time with a different parameter value.The increment
method uses its parameter to decide how much to increase the count
value.
At the end of Chapter 17, I complain about the repetitive code in Listing 17-5. When you see repetitive code, you can think about creating a method. You replace each repetition with a call to that method. The code’s logic lives in one place (the method declaration), and the method calls repeatedly refer to that place. Et voilà! The repetition problem is solved!
Modify the code in Chapter 17 as follows:
getTotal
method to the Purchase
class code in Listing 17-2. The getTotal
method takes no parameters and returns a double
value.getTotal
method calls.More specifically,
getTotal
method declares its own total
variable.getTotal
method multiplies the Purchase
object’s unitPrice
by the object’s quantity
and assigns the result to the total
variable.taxable
value, the getTotal
method either increases or doesn’t increase the total
variable’s value.getTotal
method returns the value of the total variable.In the “What’s your BMI?” experiment at the end of Chapter 17, you create a Person
class, and your main
method calculates a Person
object’s body mass index (BMI). Improve on that code so that the Person
class contains its own getBmi
method. The getBmi
method calculates the Person
object’s body mass index from the values of the object’s own weight
and height
fields.
In a separate class, the main
method creates three Person
objects, assigns values to each Person
object’s weight
and height
fields, and calls each Person
object’s getBmi
method.
In the “Nothing in particular” experiment at the end of Chapter 17, you create a Thing
class, and your main
method displays a sentence about a Thing
object. Improve on that code so that the Thing
class contains its own display
method. The display
method prints a Thing
object’s sentence based on the values of object’s own value1
and value2
fields.
In a separate class, the main
method creates three Thing
objects, assigns values to each Thing
object’s value1
and value2
fields, and calls each Thing
object’s display
method.
In the “Bit of macroeconomics” experiment at the end of Chapter 17, you create a Country
class. Your main
method asks the user for an acceptable debt-to-GDP ratio and reports That’s acceptable
or That’s not acceptable
after comparing a country’s ratio to the acceptable ratio.
Improve on that code so that the Country
class contains its own hasAcceptableRatio
method. The hasAcceptableRatio
method has one double
parameter. That parameter represents the debt-to-GDP ratio that the user has signified is acceptable. The hasAcceptableRatio
method calculates the Country
object’s debt-to-GDP ratio from the values of object’s own debt
and gdp
fields. The hasAcceptableRatio
method returns a boolean
value: true
if the Country
object’s ratio is acceptable and false
otherwise.
In a separate class, the main
method creates three Country
objects, assigns values to each Country
object’s debt
and gdp
fields, and calls each Country
object’s hasAcceptableRatio
method.
18.189.170.134