8. Methods

Polonius (Aside): Though this be madness, yet there is method in ’t.

— William Shakespeare

8.1. Problem: Three card poker

Gambling has held a fascination for humankind since its invention. As long as there have been mathematicians, they have studied the underlying mechanisms of probability and statistics that drive games of chance. A classic game of both statistics and strategy is poker. The problem we want to solve is programming one of the many variations of poker, called three card poker. Instead of bluffing, a player competes only with the house according to fixed rules without any room for psychology. A player is dealt three cards. If the player’s hand contains a pair or better, he or she wins a payoff greater than or equal to the money bet. If the player does not have a pair or better, he or she loses the money bet. Below is a table giving one possible set of payoffs for each possible hand.

Hand Payoff Hand Payoff

Straight Flush

40

Flush

2

Three of a Kind

30

Pair

1

Straight

6

Nothing

0

If you’re unfamiliar with poker rules or card games in general, here’s a quick explanation of the various hands. A traditional English or American deck of cards is made up of 52 cards, organized into four suits: spades, hearts, diamonds, and clubs. In each suit, there are 13 ranks: two, three, four, five, six, seven, eight, nine, ten, jack, queen, king, and ace.

In three card poker, a straight is when the three cards can be arranged so that their ranks (ignoring suit) are in order. For example, a hand consisting of a Four of Diamonds, a Five of Spades, and a Six of Clubs would constitute a straight. An ace can serve as either the highest rank card (coming after a king) or the lowest rank card (coming before a two) and can help form a straight in either role (but not both at the same time). A flush is when all three cards have the same suit. For example, a Three of Hearts, a Seven of Hearts, and a Jack of Hearts would constitute a flush in three card poker. A straight flush, the highest hand, is made up of cards that form both a straight (by rank ordering) and a flush (by uniformity of suit). A three of a kind occurs when all three cards have the same rank, and a pair occurs when two of the cards have the same rank. When none of these conditions hold, the hand has no special designation in three card poker, and the bet is lost.

Your task is to write a program that serves as a computerized version of the game. You must create a deck of 52 cards, thoroughly randomize them to simulate shuffling, and select the first three cards as a hand. You must then determine the highest designation that applies to the hand of cards and the corresponding winnings (if any).

Any reasonable solution to this problem uses arrays, discussed in the previous chapter. Using only that knowledge, you should be able to create a three card poker game. However, the focus of this chapter is methods, which can be used to break the solution to a problem into logical pieces. By dividing the solution in this way, we’ll be able to solve the three card poker problem in a relatively short, elegant, and easy to read way.

8.2. Concepts: Dividing work into segments

You should notice that the solutions to problems we’ve been working on have become increasingly complex as the book progresses. This progression is partly because we want to solve more interesting problems with more complex solutions but also because we’ve introduced additional Java tools that make solving these harder problems possible.

Surprisingly, the tools we introduce in this chapter do not allow you to solve a problem you couldn’t solve before. Instead, the tools in this chapter and in the next make solving a problem easier and less susceptible to errors. A relatively long, complicated list of Java statements was necessary to solve problems like the statistics program or Conway’s Game of Life. Instead of solving those problems as a single monolithic segment of code, we can break our solutions into units called static methods.

8.2.1. Reasons for static methods

A static method is a short, named segment of code that’s been packaged up so that it can be called from other parts of the program. Whenever the task performed by this method is needed, the execution of the program jumps to the code in the method, does the work it’s supposed to, and then returns to whatever it was doing before.

The reasons for using static methods can be boiled down to three essentials:

Modularity

Very little software is written by individuals. Commercial software can have tens or even hundreds of developers involved in designing, implementing, testing, and maintaining code. When the code’s divided into individual methods, those methods can be written and tested by different people with minimal interference. It’s much harder to work together on one giant block of code.

Readability

Methods are named segments of code. When a method is called, its name is used. If a meaningful name is chosen, the line in the code that calls the method helps document the code by explaining what’s happening. For example, if a method called sort() is used, a reader instantly understands that something’s being sorted. If, instead of a separate sort() method, the code that does sorting was pasted into the same location, a reader might have to devote a few moments to understanding the meaning of those lines of code, particularly without comments.

Reusability

The fact that a method can be called from anywhere in the code makes it reusable. To use the sort() example again, we might have to sort several different arrays in one program. Without methods, we’d have to keep copying and pasting code over and over again. With methods, the total amount of source code can be smaller. In fact, if you find yourself copying and pasting code, it’s often an indication that a method would be useful.

This idea of reusability stretches beyond individual programs. A static method can be called from an entirely different program. If you create a method that’s really useful, you’re free to use it in other programs you write. By doing so, you only have to makes changes in one place if you discover errors or want to increase its functionality.

8.2.2. Parallel to mathematical functions

We’ve been discussing the usefulness of static methods without saying exactly what they are. One way to get insight into methods is by acknowledging their similarity to functions from mathematics. In procedural (non-object oriented) languages like C, the equivalents of static methods are usually referred to as functions. Like functions in mathematics, a static method takes some number of inputs (possibly as few as zero) and usually produces some output.

You’ve already used several static methods from the Math class, such as Math.sqrt(). Consider the following function.

root

This function mirrors the code inside of Math.sqrt(). In math, when you apply f(x) to a specific value, you might write y = f(5). In Java, you might type the following.

double y = Math.sqrt(5);

In the statement f(5), the placeholder variable x takes on the value 5. In the same way, some variable inside of Math.sqrt() takes on the value 5 when the method is called.

It’s important to understand this similarity between methods and mathematical functions because the designers of many programming languages have been directly influenced by the older notation. However, there are many differences between the two concepts as well. Mathematical functions can take more than one input, and so can Java methods. Java methods can also take no input, while the central idea of a mathematical function is to map some input value(s) to some output value. Java methods do not necessarily even have output values. They may just do something.

8.2.3. Control flow

We have discussed selection statements such as if and switch as well as three different looping structures, for, while, and do-while. Each of these Java language features is used for control flow. The selection statements allow your program to choose which code to execute. The loops allow your code to repeatedly execute certain code. Methods also affect control flow. When a method is called, the execution of the program jumps into the method, does all the work it needs to there, and then returns to the code that called it. Recall the very simple example from above.

double y = Math.sqrt(5);

The JVM has allocated a variable of type double and is preparing to assign a value to it. Suddenly, the execution of the JVM jumps into the code inside of the Math.sqrt() method. It takes some non-trivial amount of work to compute the square root of a number. After that computation is finished, the flow of execution returns to the assignment that has been waiting all that time. Just like a mathematical function, we can treat the method call Math.sqrt(5) as if it were “magically” replaced by a double approximating the square root of 5.

8.3. Syntax: Methods

By now, you should have a good feel for the concepts behind calling and even creating static methods and are probably getting impatient to use them. However, we haven’t yet described all the details of Java method syntax. First, we’ll describe how you can create your own static methods, then we’ll discuss the finer points of calling static methods, and finally we’ll explain how class variables can be used from many different methods.

8.3.1. Defining methods

A very simple method that the Math class provides is the Math.max() method. This method selects the larger of the two values provided as input.

int maximum = Math.max(5, 10);

In this case, the value stored into maximum is 10. Despite its simplicity, we demonstrated how useful this method could be in our solution to Conway’s Game of Life from Chapter 6. If we wanted to write this method ourselves, the code would be as follows.

public static int max(int a, int b) {
    if(a >= b)
        return a;
    else
        return b;
}

Even in such a small method, there’s a lot of syntax to worry about. The first line of this method is called the method header. The public keyword in this header is used to denote that any code, even code from a different class, can call this method. We discuss restricting access to methods and variables more in the later part of this chapter. For now, assume that every method is public.

The keyword static indicates that this method is static. Although we have used the term static method many times, we have not yet defined it. A static method is linked to a whole class, not to a specific object of that class–that is, a static method can be called without referencing an object of the class. Again, we discuss the finer points of objects and classes in the next chapter. For now, all methods are static.

The third keyword in the method header is the familiar int, giving the return type of the method. Wherever this method is called, it can be treated like an int value, because that’s what it gives back. In this case, the return type is obvious: The maximum of two int values must also be an int value. Any type can be used as a return value including all the primitive types and any reference or array types. The only limitation is that a method can only return a single item, but since that item can be an array, this limitation is usually not important. It’s also possible for a method to return nothing. In that case, the keyword void is used for the return type.

Next in the method header is the identifier max, which is the name of the method. Any legal identifier that you can use for a variable name is valid for a method name as well. It’s important to pick a name that’s readable and gives a reader a clear idea about what the method does. A common convention is to name a method using a verb phrase, indicating the operation that is being done by that method (e.g., computeTax). Like variable names, the Java standard is to use camel notation, starting with a lowercase letter and capitalizing the first letter of each new word in the name.

After the name of the method is the list of parameters, separated by commas. In this case, each parameter has the int type. You’re free to name your parameters whatever you want, though they should be meaningful. You can have as few as zero parameters, but there’s an upper limit imposed by the JVM, usually 255. The body of the method follows the header of the method, surrounded by braces ({ }). Unlike if statements and loops, the braces for methods are required.

Inside the body of a method, the usual rules for Java control flow apply. Each line is executed one by one unless there are selection statements or loops. Calling methods inside of methods is also allowed. In the max() method, we use an if-else construction to find the larger of a and b. A return statement immediately stops execution of the method, transfers execution back to the calling code, and gives back the value that comes after it. In this case, the value of a is returned if it is equal or larger, and the value of b is returned otherwise. Because a return statement immediately jumps out of a method, we could have written the method with one fewer line of code.

public static int max(int a, int b) {
    if(a >= b)
        return a;
    return b;
}

The only way that the line return b; can be reached is if a had not already been returned.

Beginner programmers sometimes ask what to do if a and b are equal. In our code it’s clear that the value of a will be returned, but it hardly matters: two equal int values are indistinguishable from each other. Some students imagine handling this special case by printing an error message, but that approach is seldom useful. When trying to find the larger of two numbers in programming, it’s usually the value we want, not information about which one is actually larger.

The main() method

If some of this syntax seems eerily familiar, remember that you’ve been coding static methods since your very first Java program. The main() method is just another static method, special only because the JVM chooses to start execution there. Let’s look at the main() method from a standard Hello, World! program.

public static void main(String[] args) {
    System.out.println("Hello, world!");
    return;
}

Just like the max() method, the header for main() starts with public static. Then, the return type for main() is void because the JVM is not expecting to get any answer back. The main() method has a single parameter, an array String values. In this program, we do not use the args parameter, but it is available. For the main() method, this declaration is fine, because main() has to be uniform across all programs. However, when designing your own methods, you should not include unnecessary parameters.

The final executable line in this main() method is a return statement. Because main() has a void return type, the return statement has no value to return. For void methods, a return statement is optional. You can use it to leave a method early if desired. For a value-returning method, execution must reach a return statement with a valid value no matter what the input of the method is. If Java finds a way that execution could reach the end of a value returning method without reaching a return statement, it causes a compiler error.

Overloaded methods

Since this declaration is in another class, it’s fine to create a max() method even though there’s already one in the Math class. However, it’s possible to create more than one method with the same name even in the same class, provided that their signatures are not the same. Two methods have the same signature if they have the same name and parameter types.

public static int max(int a, int b, int c) {
    return max(max(a, b), c);
}

In this example, we’ve created yet another max() method, but this one takes three parameters instead of two. This method even calls the two parameter version of max(). Creating more than one method with the same name is called overloading those methods. Overloading methods is useful because it allows you to use the same method name for similar functionality, even when there are some underlying differences in the implementation. For example, the Math class provides four different, overloaded versions of the max() method, specialized for int, long, float, and double values, respectively.

There are limitations on creating overloaded methods, of course. The compiler must be able to determine which method you intend to use. Thus, the signatures have to vary by type or number of parameters. A different return type is not enough.

8.3.2. Calling methods

After a method has been defined, it must be called before it does anything. You have plenty of experience calling static methods like Math.sqrt() and Math.max(). An example of the appropriate syntax was given earlier.

int maximum = Math.max(5, 10);

Formally, the call starts with the name of the class (Math), followed by a dot, followed by the name of the method (max), followed by the list of arguments inside parentheses. These arguments are the values you want to pass into the method. Some books use the term formal parameters to describe the variables defined in the method signature and actual parameters to describe the values passed into the methods, but we stick with the simpler terms parameters and arguments.

Of course, the number of arguments must match the number of parameters defined by the method, and the types must match as well. Java performs automatic casting when no precision is lost. Thus, you can always supply an int argument for a double parameter but not the reverse. Arguments can be literal values, variables, or even other method calls that return the appropriate type.

Using the max() method defined before, we could rewrite our simple example without a class name.

int maximum = max(5, 10);

Whenever you call a static method from code that’s inside the same class, you can leave out the class name.

Binding

Many new programmers are confused about the relationship between arguments and parameters. The process of supplying an argument to be used as a parameter is called binding. Through binding, a value or variable from the calling code is given a new name inside of a method. Consider the following method.

public static int add(int a, int b) {
    return a + b;
}

This absurdly short method adds two numbers together and returns the result, approximating the functionality of the + operator. We could call the method in the following context.

int x = 3;
int y = 5;
int z = add(x, y);

Inside the method, the value of x is bound to the variable a, and the value of y is bound to the variable b. The add() method has its own scope. Scope means the area where a variable name is visible (or meaningful). Thus, x and y do not exist inside of the add() method, only the variables a and b do. Since methods have their own scope, variables in one method can have the same names as variables in another method without the compiler (or the programmer!) becoming confused. Consider the following example.

int a = 3;
int b = 5;
int c = add(b, a);

Here the variables a and b exist in both the calling code and inside the method, but the names are independent. The value of a in the calling code happens to be bound to a variable called b inside the method, but the JVM has no confusion about which a is which. Herein lies the value of methods: They are largely independent of whatever else is going on in the code, allowing the programmer to focus on a small, manageable task.

Another important feature of Java is that the process of binding variables is pass by value, meaning that only the value of the argument is bound to the parameter. Whenever a method is called, the method creates a new variable for each parameter and copies the value of its argument into it. In practice, this approach means that a method cannot directly change the value of an argument. Consider the following method.

public static void increment(int counter) {
    counter++;
}

This method takes the value of its argument and copies it into the new variable counter. Then, it increments counter, but the original argument is unchanged. Thus, the following fragment is an infinite loop.

int i = 0;
while(i < 100)
    increment(i);

The value of i remains fixed at 0 for the entire program. The copy of i bound to counter increases to 1 every time increment() is called, but i remains unaffected.

This is not to say that a method cannot affect the variables outside of itself. The primary way that it can do so is by using return statements. We can rewrite increment() to achieve this effect.

public static int increment(int counter) {
    return counter + 1;
}

Then, we need to adjust the loop so that it stores the returned value instead of dropping it on the floor.

int i = 0;
while(i < 100)
    i = increment(i);

A second way that methods can affect the values of outside variables is more indirect. In Java, every argument is passed by value, even arrays and objects. Practically, this means that, if a reference to an array is passed into a method, you cannot change which array it is pointing at. Since references are not values but names pointing at particular locations in memory, you can directly change the contents of that memory with a method, even if you can’t change which locations are being referenced. For example, the following method does not reverse the order of an array.

public static void badReverseArray(int[] array) {
    int[] temp = new int[array.length];
    for(int i = 0; i < array.length; i++)
        temp[i] = array[array.length - i - 1];
    array = temp;
}

Although this code does store a reversed version of array in temp, the last line of the method is meaningless: The array passed into the method still points to the original location in memory. We can rewrite the method to do the reversal in place, meaning that the values of the array are shuffled around, but the array still occupies the same memory locations.

public static void goodReverseArray(int[] array) {
    int temp;
    for(int i = 0; i < array.length / 2; i++) {
        temp = array[i];
        array[i] = array[array.length - i - 1];
        array[array.length - i - 1] = temp;
    }
}

In this version of the method, we swap the first element of the array with the last, the second with the second to last, and so on. We only go up to the halfway point of the array, otherwise we would undo the reversal process. The values of the array are reversed, but they still occupy the same chunk of memory. It’s possible to write a correct method more in the style of badReverseArray() which creates a temporary array, copies the original values into it, and then copies them back to the original array in reverse order, but it’s less efficient to create the extra array and perform two copies.

8.3.3. Class variables

According to the rules we’ve given so far, the only legal variables in the scope of a static method are the parameters and any other local variables declared inside the method. However, it’s possible to create a variable that exists outside of static methods yet is visible inside all of them. These kinds of variables are called class variables (or sometimes static fields or global variables). These variables persist between method calls. The syntax for creating such a variable is to declare it outside of all methods (but inside the class) with the keyword static and an access modifier such as public or private. For example, the following class includes a method called record() that increases the class variable counter every time it’s called.

Program 8.1 Keeps track of the number of times the record() method is called.
public class Bookkeeper {
    public static int counter = 0;

    public static void main(String[] args) {
        while(Math.random() > 0.001)
            record();

        System.out.println("Record was called " + counter + " times.");
    }

    public static void record() {   
        counter++;  
    }
}

When run, this program calls the record() method some random number of times, and the variable counter keeps track of the number. Because counter is static, it’s accessible to both the main() and record() methods. Many programmers frown on the use of class variables precisely because they’re visible to many different methods. The idea of a method is to isolate pieces of code so that the complexity of a program can be divided into simple units. In the case of a public class variable, even code in other classes can modify its value. If many different pieces of code can modify a variable, it may be difficult to keep that variable from being changed in unexpected ways. For example, if another method used the counter variable to record the number of times it was called, the final value of counter would be the sum of the number of times the two methods were called. There might be some reason to keep track of such information, but it would be impossible to reconstruct what fraction of the value in counter came from one method and what fraction came from the other.

Class variables have their uses, but they should generally be avoided. The chief exception to this rule is constants. Since a constant never changes, a class variable is a great place to store it, making the value available to all code. An example you’ve already used is Math.PI. As with static methods, a static field from another class can be accessed by using the class name, then a dot, and then the name of the static field. Again, when the code using the field is in the same class, the class name can be dropped. A class constant is declared like a class variable, but with the addition of the final keyword.

Example 8.1 Gravitational attraction

The following class allows a user to compute the one-dimensional force due to gravity. This force F is given by the following equation.

gravitation

In this equation, m1 is the mass of one object, m2 is the mass of another, r is the distance between their centers, and G is the gravitational constant, 6.673 × 10-11 N⋅m2⋅kg-2.

Program 8.2 Calculates the attraction due to gravity between two masses.
import java.util.Scanner;

public class Gravity {
    public static final double G = 6.673e-11;

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        System.out.println("What is the first mass?");
        double m1 = in.nextDouble();
        System.out.println("What is the second mass?");
        double m2 = in.nextDouble();
        System.out.println("What is the distance between them?");
        double r = in.nextDouble();
        System.out.println("The force of gravity is " + force(m1, m2, r) + " N");
    }

    public static double force(double m1, double m2, double r) {    
        return G*m1*m2/(r*r);
    }
}

Use named constants whenever they increase readability. You can use the public modifier if you want all classes to have access to your constant (Gravity.G is a good example). You can use the private modifier if you want the constant to be accessible only inside your class, if it has no use outside, or if it contains secret information.

8.4. Examples: Defining methods

Any large problem should be broken down into methods. Because the technique is useful in so many circumstances, it’s difficult to give a set of examples that covers all cases. Instead, our examples are short, easy to understand methods, focusing on Euclidean distance, testing for palindromes, and converting a String representation of an int to an int.

Example 8.2 Euclidean distance

The Euclidean distance between two points is the length of a straight line connecting them. It plays an important role in 3D graphics and games and is the basis for many other practical applications involving spatial relationships.

Given two points in 3D space (x1, y1, z1) and (x2, y2, z2), we can compute the Euclidean distance between them with the following equation.

distance

The method below applies this equation directly.

public static double distance(double x1, double y1, double z1,
						double x2, double y2, double z2) {
    double x = x1 - x2;
    double y = y1 - y2;
    double z = z1 - z2;
    return Math.sqrt(x*x + y*y + z*z);
}

This equation is a good candidate for a static method since it might be necessary to do this calculation many times and it does not depend on any other variables or program state.

Example 8.3 Palindrome testing

A palindrome is a word or phrase (or even a number) that’s the same spelled forward and backward. “Racecar,” “Madam, I’m Adam,” and “Satan, oscillate my metallic sonatas” are examples in English. Typically, spaces and punctuation are ignored. We’re going to write a function that, given a String, returns true if it’s a palindrome and false otherwise. To simplify the problem, we’re not going to ignore spaces and punctuation. Thus, with our method, “racecar” counts as a palindrome, but neither of the other two examples would.

public static boolean isPalindrome(String text) { (1)
    text = text.toLowerCase(); (2)
    for(int i = 0; i < text.length() / 2; i++) (3)
        if(text.charAt(i) != text.charAt(text.length() - i - 1)) (4)
            return false;
    return true;
}
1 Because our method returns true or false, its return type must be boolean. Many methods that return a boolean value have a name starting with is, like our method.
2 The first line of the body of our method changes text to lowercase. The String method toLowerCase() creates a lowercase copy of the String it’s called on, in this case text. Then, we point the reference variable text at that new, lowercase String. On the outside of this function, the String passed in does not change because the reference text is passed by value.
3 The loop iterates through the first half of text, comparing it to the second half. This loop reflects the asymmetry of these kinds of tests: You can’t be sure that text is a palindrome until you’ve checked the entire thing, but you immediately know that it’s not if even a single pair of characters doesn’t match.
4 If the test in that if statement ever shows that the two char values aren’t equal, the return false statement jumps out of the method without completing the loop.
Example 8.4 Parsing a number

When you read in a number using an object of the Scanner class, it converts (or parses) the text entered by the user into the appropriate type. For example, the nextDouble() method reads in some text and convert it into a double. When you use a JOptionPane method to read in input, it comes in as a String. If you want to use that data as a double, you must convert it using the Double.parseDouble() static method. Some Java programmer had to write this method. We’re going to recreate a similar method to convert the String representation of a floating-point number into a double. Our simple method ignores scientific notation.

public static double parseDouble(String value) {
    int i = 0;
    boolean negative = false;
    double temp = 0.0;
    double fraction = 10.0;
    if(value.charAt(i) == '-') { (1)
        negative = true;
        i++;
    }
    else if(value.charAt(i) == '+')
        i++;
    while(i < value.length() && value.charAt(i) != '.') { (2)
        temp *= 10.0;
        temp += value.charAt(i) - '0';
        i++;
    }
    i++; //move past decimal point (if there) (3)
    while(i < value.length()) { (4)
        temp += (value.charAt(i) - '0') / fraction;
        fraction *= 10.0;
        i++;
    }
    if(negative) (5)
        temp = -temp;
    return temp;
}
1 After declaring a few variables, this method first checks index 0 in the input String value to see if it is a '-' or a ''`. If it is a `'-'`, it sets `negative` to `true` and moves on. If it is a `'', it simply moves on.
2 Then, the method loops through value until it reaches the end or reaches a decimal point. As it iterates, it multiplies the current value of temp by 10.0 and adds in the next digit from value (after subtracting '0' so that the range is is from 0 to 9). This repetitive multiplication by 10.0 accounts for the increasing powers of 10 in the base 10 number system. Since temp starts with a value of 0.0, the first multiplication has no effect, as intended.
3 After the first while loop, the index i is incremented once, to skip the decimal point, if there is one. If there is no decimal point, the loop must have exited because the end of value had been reached.
4 The second while loop runs to the end of value, this time adding in each digit value divided by fraction, which is increased by a factor of 10.0 each time. Doing so allows us to add smaller and smaller fractional digits to the total.
5 We set temp to its opposite if the flag negative was set earlier and finally return temp.

Note that the real Double.parseDouble() method not only accepts String values in scientific notation but also does a great deal of error checking. Our code either crashes or gives inaccurate results on an empty String, a String containing non-numerical characters, or a String with more than one decimal point. Furthermore, this code does not use the best approach for minimizing floating-point precision errors.

8.5. Solution: Three card poker

Here we present our solution to the three card poker problem. We explain each method individually.

public class ThreeCardPoker {
    public static final String[] SUITS = {"Spades", "Hearts", (1)
        "Diamonds", "Clubs"};
    public static final String[] RANKS = {"2", "3", "4", "5", "6",
        "7", "8", "9", "10", "Jack", "Queen", "King", "Ace"};   
    public static final int STRAIGHT_FLUSH = 40; (2)
    public static final int THREE_OF_A_KIND = 30;
    public static final int STRAIGHT = 6;
    public static final int FLUSH = 2;  
    public static final int PAIR = 1;
    public static final int NOTHING = 0;
1 Before our main() method begins, we declare a number of class constants. Two constant arrays of String values provide us with an easy way to represent suits and ranks.
2 The remaining six int constants are used to allocate a winning payoff to each possible outcome.

Note that these constants can be declared anywhere inside the class, provided that they’re outside of methods. However, it’s typical (and good style) to declare them at the top of the class.

    public static void main(String[] args) {
        int[] deck = new int[52]; (1)
        int[] hand = new int[3];                
        for(int i = 0; i < deck.length; i++) (2)
            deck[i] = i;
        shuffle(deck);
        for(int i = 0; i < hand.length; i++) (3)
            hand[i] = deck[i];      
        int winnings = score(hand); (4)
        System.out.println("Hand: ");
        print(hand);
        if(winnings == 0) (5)
            System.out.println("You win nothing.");
        else
            System.out.println("You win " + winnings +
            " times your bet.");
    }
1 In the main() method, an array representing a deck of 52 cards is created first, followed by an array representing the 3 cards to be dealt.
2 The deck is filled sequentially and then shuffled with a method.
3 Next, the first 3 cards of the deck are copied into the array representing the hand of cards.
4 The score of the hand is determined, and then the hand is printed out. We print the hand after determining the score because the hand is sorted in the process of determining the score, making the output easier to read.
5 Finally, we print the appropriate output, depending on the score.
    public static void shuffle(int[] deck) {
        int index, temp;
        for(int i = 0; i < deck.length; i++) {
            index = i + (int)((deck.length - i)*Math.random());
            temp = deck[index];
            deck[index] = deck[i];
            deck[i] = temp;
        }       
    }

This method shuffles the deck. Its approach is to swap the first element in the array of cards with one of the elements that follow, chosen randomly. Then, it swaps the second element in the array with any of the elements that follow it, and so on. If Math.random() truly gives us a uniformly generated random number in the range [0,1), the final shuffled deck should be any one of the 52! possible decks with equal probability.

    public static void print(int[] hand) { (1)
        for(int i = 0; i < hand.length; i++)
            System.out.println(RANKS[getRank(hand[i])] + " of "
            + SUITS[getSuit(hand[i])]);
    }

    public static int getRank(int value) { return value % 13; } (2)
    public static int getSuit(int value) { return value / 13; } (3)
1 The first of these methods prints out a human readable version of each card in an array (instead of 0 - 51). It does so using the second and third methods as helper methods.
2 Method getRank() computes the rank of a card from its number.
3 Method getSuit() computes the suit of a card from its number.

The indexes obtained from these methods are used to index into the RANKS and SUITS arrays.

In the C language, calling the equivalent of a method from a method defined earlier requires a special declaration step called prototyping before both methods. Java does not have this complication, and the getRank() and getSuit() methods compile and function perfectly if they are written above print() or below it inside the class definition.

    private static int score(int[] hand) {  
        sortByRank(hand); 
        if(hasStraight(hand) && hasFlush(hand))
            return STRAIGHT_FLUSH;
        if(hasThree(hand))
            return THREE_OF_A_KIND;
        if(hasStraight(hand))
            return STRAIGHT;
        if(hasFlush(hand))
            return FLUSH;
        if(hasPair(hand))
            return PAIR;        
        return NOTHING;
    }   

This method computes the score by first sorting the hand and then testing progressively worse outcomes, starting with the best, a straight flush. As it moves down the list of outcomes, it calls appropriate methods to determine if a hand has a certain characteristic.

    private static void sortByRank(int[] hand) {
        int smallest, temp;
        for(int i = 0; i < hand.length - 1; i++) {
            smallest = i;
            for(int j = i + 1; j < hand.length; j++) {
                if(getRank(hand[j]) < getRank(hand[smallest]))
                    smallest = j;
            }
            temp = hand[smallest];
            hand[smallest] = hand[i];
            hand[i] = temp;
        }
    }

This code is an implementation of selection sort packaged into a method. Note that this method does change the values inside of the array hand even though it cannot change the array that hand points to. The array itself is passed by value, but its contents are effectively passed by reference.

    private static boolean hasPair(int[] hand) { (1)
        return getRank(hand[0]) == getRank(hand[1]) ||
               getRank(hand[1]) == getRank(hand[2]);
    }

    private static boolean hasThree(int[] hand) { (2)
        return getRank(hand[0]) == getRank(hand[1]) &&
               getRank(hand[1]) == getRank(hand[2]);
    }

    private static boolean hasFlush(int[] hand) { (3)
        return getSuit(hand[0]) == getSuit(hand[1]) &&
               getSuit(hand[1]) == getSuit(hand[2]);
    }

    private static boolean hasStraight(int[] hand) { (4)
        return (getRank(hand[0]) == 0 && getRank(hand[1]) == 1
                && getRank(hand[2]) == 12) || //ace low
               (getRank(hand[1]) == getRank(hand[0]) + 1 &&
                getRank(hand[2]) == getRank(hand[1]) + 1);
    }   
}
1 The code in hasPair() works by checking to see if the first and second or second and third cards have the same rank. An extra condition would be required if the cards weren’t sorted.
2 The code in hasThree() checks to see if all the ranks are the same.
3 The code in hasFlush() is the same as hasThree() except that it checks for suit instead of rank.
4 Finally, hasStraight() checks to see if the ranks come one after the other, with an extra case to deal with the possibility of the ace counting as low. This test only works because the cards were sorted previously.

These four methods would be similar but more complex for five- or seven-card poker hands.

8.6. Concurrency: Methods

In Java, it’s impossible to have concurrency without methods. Methods are the way we break a large program into manageable pieces but are also part of the syntax that Java uses to create threads of execution. Each thread of execution is associated with a Thread object; however, creating the object is not enough to start a new thread of execution running. Only when the start() method is called on the Thread object does the new thread start running.

Hopefully, you’ve begun to visualize the execution of Java programs as an arrow that sits next to each line of code as it’s executed. This arrow can jump to a choice and skip over other code using if and switch statements. Using loops, the arrow can jump backward and repeatedly execute code it’s just executed. As we discussed in this chapter, the arrow can jump into a method, execute the code in that method, and then return to its caller, going back right to where it left off before the call.

When the start() method is called on a Thread object, however, the arrow returns to the caller, but it also splits itself into a second arrow that then executes the corresponding run() method and any other methods it calls. Note that we’re talking about a method called on a Thread object, not a static method called on the class as a whole. Calling start() is an instance method, which we discuss in Chapter 9. Unlike the static methods in this chapter, an instance method is tied to a particular object, but most of what you’ve learned about methods still applies.

Methods are supposed to make programming easier by breaking programs into chunks small enough to think about. One of the only real dangers of methods is using class variables, as mentioned in Section 8.3.3. This problem becomes worse with multiple threads. With a single thread, two or more different methods can all affect the same class variable, perhaps in conflicting ways. With multiple threads, even the same method can interfere with itself.

Example 8.5 LCG without thread safety

A linear congruential generator (LCG) allows you to create a sequence of pseudorandom numbers using the equation xi = (axi-1 + b) mod m, deriving the next number from the previous one, and so on.

Program 8.3 Implements an LCG similar to one sometimes used in the function rand() used in the C language.
public class UnsafeRandom {
    private static int next = 1;
    private final static int A = 1103515245;
    private final static int B = 12345;
    private final static int M = 32768;
    
    public static int nextInt() {
        return next = (A*next + B) % M;
    }
}

The UnsafeRandom program listed above always generates the same sequence of pseudorandom numbers, which can be useful for debugging a program. However, if two or more threads call nextInt(), they’ll probably get different sequences. One thread will pick up some of the numbers, and the other will pick up the missing numbers in between. If each thread wants to generate the same sequence of numbers, the method should be rewritten so that it takes in the previous number in the sequence. In that way, there’s no shared state. Remember that using a (non-final) class variable should be avoided whenever possible.

Program 8.4 Implements the same LCG safely by requiring the caller to supply the previous random number.
public class SafeRandom {   
    private final static int A = 1103515245;
    private final static int B = 12345;
    private final static int M = 32768;
    
    public static int nextInt(int previous) {
        return (A*previous + B) % M;
    }
}
Example 8.6 Unpredictable methods

By forcing each thread to carry its own state, we fixed the previous problem. In Chapter 14 we’ll talk about the much nastier problem of two threads executing a method at exactly the same time. When that happens, very curious effects are possible. Consider the following program.

Program 8.5 The print() method always prints "Even" when run with a single thread but can sometimes print "Odd" if called repeatedly with multiple threads.
public class AlwaysEven {
    private static int value = 1;

    public static void print() {        
        value++; (1)
        if(value % 2 == 0)
            System.out.println("Even");
        else
            System.out.println("Odd");
        value++; (2)
    }
}
1 With a single thread running, value always goes up to an even number before printing.
2 Afterward, it increments to the next odd number.

If two or more threads call the print() method, value could be changed by one right before the other executes the if statements.

8.7. Exercises

Conceptual Problems

  1. Describe three advantages of dividing long segments of code into static methods.

  2. Can you think of any disadvantages of dividing code into methods? Are there situations in which using a method is unwise?

  3. If you wanted to declare a static method that would compute the mean, median, and standard deviation of an input array of double values, how would you return those three answers?

  4. Consider the following method definition.

    public static void twice(int i) {
        i = 2 * i;
    }

    How many times does the following loop run, and why?

    int x = 2;
    while(x < 128)
        twice(x);
  5. Consider the following signatures of two overloaded methods.

    public static int magic(int rabbit, double hat)
    public static int magic(double wand, int spell)

    Which method would be invoked by the following call?

    int x = magic(3, 16);

    What about the following?

    int y = magic(3.2, 16.4);

    Use a compiler to check your answers.

  6. The following class generates a sequence of even numbers. Each time the next() method is called, the next even number in the sequence is returned. What’s the design problem with using a static field to keep track of the next value in the sequence?

    public class EvenNumbers {
        private static int counter = 0;
    
        public static int next() {
            counter += 2;
            return counter;
        }
    }

Programming Practice

  1. Write a static method called cube() that takes a single double value as a parameter and returns its value cubed. Do not use the Math.pow() method.

  2. Implement a static method that takes a single int value as a parameter and prints its digits in reverse. For example, if 103 was passed into this method, it would print 301 to the screen.

    You can find out what digit is in the ones place of a number by taking its remainder modulus 10. Then, you can remove the digit in the ones place by dividing by 10. Do not convert the int value into a String.

  3. Write a static method that takes an array of int values as a parameter and returns true if the array is in ascending order and false otherwise. Compare each element of the array to the next element of the array. If the current element is ever larger than the next element, the array is not sorted in ascending order. Note that you can only be sure that the array is in ascending order after you have checked all neighboring pairs.

  4. Write a static method that finds the ⌊log2 n⌋ of an integer n. Note that if log2 n = x, it’s also true that n = 2x. In other words, the log2 operator tells you what power of 2 a number is. One way to define the log2 n is the number of times you have to divide n by 2 to get 1. Use this definition to make a loop that finds the value without using any calls to the Math library.

    Here are some examples of the return values your method should give for various input values of n.

    n Return Value n Return Value

    1

    0

    16

    4

    2

    1

    100

    6

    4

    2

    512

    9

    8

    3

    1000

    9

    10

    3

    1024

    10

  5. Write a method that tests palindromes like the method from Example 8.3 but also ignores punctuation and spaces. Thus, "A man, a plan, a canal: Panama" should be counted as a palindrome by this new method.

  6. Add to the parseDouble() method from Example 8.4 so that it can also handle numbers in standard Java scientific notation, such as 7.239e-14. Note that the e can be uppercase or lowercase, and the exponent can begin with a minus sign (-), a plus sign (+), or neither.

  7. Re-implement the solution from Section 8.5 so that it uses a GUI constructed with JOptionPane to display the hand and the winnings.

  8. Five card poker is a much more common version of poker than the three card version we discussed in Section 8.1. Using static methods, implement a two-player game of poker in which the deck is shuffled and then dealt into two hands of five cards each. Then, state which player’s hand wins. With five cards, determining which hand wins is a more complicated process. The rankings of the various possible hands from best to worst are as follows.

    Straight Flush

    All five cards belong to the same suit and have ranks in sequential order (with either ace high or low). If two people both have straight flushes, the higher ranked one wins. If they both have the same ranks, it is a tie.

    Four of a Kind

    Four of the five cards have the same rank. If two people have four of a kind, the higher rank set of four wins.

    Full House

    Three of the five cards have the same rank and the other two share another rank. If two people have a full house, the higher ranked set of three wins.

    Flush

    All five cards have the same suit. If two people have flushes, the one with the highest card wins. If the highest card is a tie, the next highest is the tie breaker, and so on. If the two flushes have exactly the same ranks, the two flushes tie.

    Straight

    All five cards have ranks in sequential order (with either ace high or low). If two people both have straight, the higher ranked one wins. If they both have the same ranks, it is a tie.

    Three of a Kind

    Three of the cards have the same rank. If two people have three of a kind, the higher ranked set of three wins.

    Two Pair

    A pair of cards have the same rank and another pair of cards share another rank. If two people both have two pairs, the higher ranked pair is a tiebreaker. If the higher ranked pair is the same, the lower ranked pair is a tiebreaker. If the lower ranked pair is the same, the final unpaired card is the tiebreaker. If all the ranks of both hands match, it is a tie.

    Pair

    A pair of cards has the same rank. If two people have pairs, the rank of the pair is a tiebreaker. If the pairs have the same rank, the remaining cards in each hand are tiebreakers, in descending rank order.

    High Card

    If none of the other cases hold, the high card determines the value of the hand. If two people have the same highest card, the remaining cards in each hand are used as tiebreakers, in descending rank order.

Experiments

  1. In terms of time, there’s a small overhead associated with calling a method and returning a value, but it’s very hard to measure. Write a program with two int variables, a and b, where a starts with a value of 1 and b starts with a value of 2. Run a for loop 100,000,000 times. On each iteration first increase the value of a by the value of b and then increase the value of b by a. Time this loop with System.nanoTime(), and then print out the time taken and the value of a. The value of a is not important, but the compiler will optimize away the math done with a and b unless we output the value. We recommend that you run this program repeatedly to get a sense of the average running time.

    Now, instead of using the + operator to add a and b, use the following method.

    public static int add(int a, int b) {
        return a + b;
    }

    Again, run your program repeatedly with this modification. What’s the difference in running time between the version that uses a method and the version that does addition directly?

    Depending on your JVM, it’s quite possible that there’s almost no difference. The JVM does a lot of optimizations including inlining, which replaces a call to a short method with the actual code inside the method.