Chapter 4
Interactive Programs

Copyright © 2004 by Stuart Reges

4.1 Introduction

The last three chapters examined programs that produce output. This chapter examines the more interesting side of communication: programs that interact with a user. Because humans and computers don't communicate very well, these programs are the most challenging to write.

The chapter begins with an examination of how values are obtained from the user and the basic structure of an interactive program. After covering this basic background, you will learn an important new control structure: the if/else. Called a conditional or selection construct, it allows you to select from among alternatives depending upon certain conditions. This new structure, like the for loop, is so powerful that you will wonder how you managed to write programs without it.

The chapter ends by discussing how we can test before we read to make sure that reading will succeed and how to indicate that a serious error has occurred during program execution.

4.2 The Scanner Class

We have seen that by calling System.out.println and System.out.print we can produce console output rather easily. When you say System.out you are referring to an object in the System class known as the standard output stream, or "standard out" for short. There is a corresponding object for standard input known as System.in. But Java wasn't designed for console input and System.in has never been particularly easy to use for console input.

The new release of Java (version 5.0) has a class called Scanner that simplifies reading from the console and reading from files. It will be included in the java.util package. In the meantime, we will be using our own version of Scanner. It isn't a full implementation of the Scanner class, but it has the most important methods and it will allow us to practice this tool that has become part of the Java class libraries.

To use Scanner you first have to construct one. You can construct a Scanner by passing it a reference to an input stream. To read from the console, we pass it System.in:

    Scanner console = new Scanner(System.in);
Once constructed, you can ask the Scanner to return a value of a particular type. There are a number of different methods that all begin with the word "next" to obtain these various types of values.

Scanner Methods
Method Description
next()reads and returns the next token as a String
nextBoolean()reads and returns a boolean value
nextDouble()reads and returns a double value
nextFloat()reads and returns a float value
nextInt()reads and returns an int value
nextLine()reads and returns the next line of input as a String
nextLong()reads and returns a long int value
nextShort()reads and returns a short int value

The methods we will be most interested in are next(), nextDouble(), nextInt() and nextLine(). Typically we will use a variable to keep track of the value returned by one of these methods. For example, we might say:

    int first = console.nextInt();
    int second = console.nextInt();
    int third = console.nextInt();
These statements read in values for three variables. When your program encounters these "next" calls on Scanner, it pauses and waits for a user to type a line of input in the console window. Whenever the computer pauses for input, it will pause for an entire line of input.

When executing these "next" methods, the computer will wait until it has been given the correct amount of data. If a user types in only two numbers when there are three calls on nextInt(), the computer will pause and wait for the user to type a second line. If after reading this second line, it doesn't have enough values, the computer will wait for the user to type a third line, and so on.

You can use the Scanner class to read input line by line using the nextLine() method. The other "next" methods are all token based.

Token

A single element of input (e.g., one word, one number).

By default, the scanner uses whitespace to separate tokens.

Whitespace

Spaces, tab characters and "new line" characters.

Scanner looks at what the user types and uses the whitespace on the input line to break it up into individual tokens. For example, the following line of input:

    hello      there. how are         "you?"  all-one-token
would be split into six tokens:

    hello
    there.
    how
    are
    "you?"
    all-one-token
Notice that Scanner includes punctuation characters like periods, question marks, dashes and quotation marks in the tokens it generates. Also notice that we got just one token for "all-one-token". That's because there is no whitespace in the middle to break it up into different tokens. You can control how Scanner turns things into tokens (a process called "tokenizing" the input), but we won't be doing anything that fancy.

If a user types something that isn't an integer when you're calling nextInt(), such as XYZZY, the Scanner object generates something called an exception.

Exception

An error that prevents a program from continuing its normal execution.

We say that an exception is "thrown" when an error is encountered. When an exception is thrown, Java looks to see if you have written code to handle it. If not, program execution is halted and you will see what is known as a "stack trace" or "back trace." The stack trace shows you the series of methods that have been called in reverse order. We will see more about exceptions in a later chapter. For now, though, we will assume good user behavior.

4.2.1 Elements of an Interactive Program

Now that you know how to read information from the user, you can write your first interactive program. Here is a simple program that reads in two integers and does some calculations.

// This program prompts the user for two integers and reports various // arithmetic calculations using those numbers. public class SimpleMath { public static void main(String[] args) { System.out.println("This program does some simple math on two"); System.out.println("integers. If you give me two numbers to work"); System.out.println("with, I'll do some calculations for you."); System.out.println(); Scanner console = new Scanner(System.in); System.out.print("Give me two numbers---> "); int first = console.nextInt(); int second = console.nextInt(); System.out.println(); System.out.println(first + " + " + second + " = " + (first + second)); System.out.println(first + " - " + second + " = " + (first - second)); System.out.println(first + " * " + second + " = " + (first * second)); System.out.println(first + " / " + second + " = " + (first / second)); System.out.println(first + " % " + second + " = " + (first % second)); System.out.println(second + " / " + first + " = " + (second / first)); System.out.println(second + " % " + first + " = " + (second % first)); } } The program starts by explaining what is going to happen. This is essential for interactive programs. If calls on Scanner "next" methods appear first, users would be expected to type input without knowing what to type. The fourth statement generates a blank line of output to separate the introductory text from what will follow.

The fifth statement constructs the Scanner object and the sixth is a prompt, a request for information from the user. We use a print statement instead of a println so that the user will type the values on the same line as the prompt (i.e., to the right of the prompt).

After prompting for values, the program reads in two numbers and writes out the results. The execution of this program would go something like this:

This program does some simple math on two integers. If you give me two numbers to work with, I'll do some calculations for you. Give me two numbers---> 29 536 29 + 536 = 565 29 - 536 = -507 29 * 536 = 15544 29 / 536 = 0 29 % 536 = 29 536 / 29 = 18 536 % 29 = 14

4.3 Cumulative Sum

A common programming task is finding the sum of a series of numbers. The program in the last section added two numbers together by storing each value in its own variable. It would not be convenient, however, to define one hundred variables to add together one hundred numbers.

The trick is to keep a running tally and to process one number at a time. If you have a variable called sum, for example, you would add in the next number by saying:

	sum = sum + next;
or using the shorthand assignment operator:

	sum += next;
This statement says to take the old value of sum, add the value of a variable called next, and store this as the new value of sum. This operation is performed for each number to be summed. There is a slight problem when executing this statement for the first number, because there is no old value of sum the first time around. To get around this, you initialize sum to a value that will not affect the answer--zero.

Rather than obtaining all the numbers first and then doing all the processing, you alternate obtaining a number and processing it. This allows you to use a single variable next rather than many variables. Here is a pseudocode description of the cumulative sum algorithm:

	sum = 0.
	for (all numbers to sum) {
	    obtain "next".
	    sum += next.
        }
To implement this algorithm, you must decide how many times to go through the loop and how to obtain a next value. Here is an interactive program using both doubles and ints that prompts the user for how many numbers to sum together and for the numbers themselves.

// This program adds together a series of numbers for the user. It first // asks the user how many numbers to sum, then asks for the series of // numbers and reports the sum. public class FindSum { public static void main(String[] args) { System.out.println("This program will add together a series of real"); System.out.println("numbers for you."); System.out.println(); Scanner console = new Scanner(System.in); System.out.print("How many numbers do you want to add together? "); int totalNumber = console.nextInt(); double sum = 0; for (int i = 1; i <= totalNumber; i++) { System.out.print(" next--> "); double next = console.nextDouble(); sum += next; } System.out.println("Sum = " + sum); } } The program will execute something like this:

This program will add together a series of real numbers for you. How many numbers do you want to add together? 5 next--> 13.5 next--> 12.85 next--> -109.8 next--> 102.45 next--> 213.8 Sum = 232.8 The cumulative sum algorithm and variations on it will be useful in many of the programming tasks you solve. How would you do a cumulative product, for example? Here is the pseudocode:

	product = 1.
	for (all numbers to multiply) {
	    obtain "next".
	    product *= next.
        }

4.4 If/Else Statements

The programs you write will often make decisions between different possible actions. The simplest way to introduce a decision branch into a program is using an if statement.

if (<test>) <statement>; An if statement allows you to put a condition on the execution of a statement. The statement will only be executed if the test evaluates to true.

    if (number > 0)
	answer = Math.sqrt(number);
This test is useful because you wouldn't want to ask for the square root of a negative number. Notice that we once again see a Java keyword ("if") followed by parentheses with no semicolon after the parentheses. This is because the if, like the for loop, is a control structure. The assignment statement is controlled by the if and, therefore, is indented to indicate the structure.

Another form of the if statement includes an else clause:

if (<test>) <statement>; else <statement>; as in:

	if (number > 0)
	    answer = Math.sqrt(number);
        else
	    answer = 0;
The statement in the "if" part is executed if the test evaluates to true, but if the test evaluates to false, the statement in the "else" part is executed. Thus, the if/else controls two different statements, one to be executed when the test returns true and the other to be executed when the test returns false.

As always, if you want to control more than one statement, you introduce curly braces. The Sun coding convention is to include the curly braces even if there is a single statement being controlled. Consider what happens when we add curly braces to the example above. Remember that Sun uses what is known as K&R style which places the open curly brace at the end of a line:

	if (number > 0) {
	    answer = Math.sqrt(number);
        }
        else {
	    answer = 0;
        }
There is a special case in K&R style for if/else statements. The code above is shortened by combining the close curly brace and else lines:

	if (number > 0) {
	    answer = Math.sqrt(number);
        } else {
	    answer = 0;
        }
This makes for a compact format and it is the format that Sun selected for the Java coding conventions. We will use this format in the examples in the book with if/else statements that involve curly braces, although keep in mind that we are not following the Sun convention of always including the curly braces.

The if/else statements are controlled by a test. Simple tests compare two expressions to see if they are related in some way. Such a test is itself an expression that returns either true or false and is of the following form:

<expression> <relational operator> <expression> To evaluate such a test, you first evaluate the two expressions and then see if the given relation holds between the value on the left and the value on the right. If the relation does hold, the test evaluates to true. If not, the test evaluates to false.

The relational operators are:

Relational Operators
Operator Meaning Example Value
== equals 2 + 2 == 4 true
!= not equals 3.2 != 4.1 true
< less than 4 < 3 false
> greater than 4 > 3 true
<= less than or equals 2 <= 0 false
>= greater than or equals 2.4 >= 1.6 true

Notice that the test for equality involves two equals characters in a row ("=="). This is to distinguish it from the assignment operator ("=").

Because you use the relational operators as a new way of forming expressions, you must reconsider precedence. The following expression is made up of the constants 3, 2, 9, and the operations plus, times and equals.

	3 + 2 * 2 == 9
Which of the operations is performed first? Because the relational operators have a lower level of precedence than the arithmetic operators, the answer is that the times is performed first, then the plus, then the "equals" test. You may recall that section 2.3.3 had a table showing the precedence of many operators. You will see in that table that the relational operators have a lower level of precedence than the multiplicative and additive operators. In other words, Java will perform all of the "math" first before it tests for these relationships. This precedence scheme frees you from parenthesizing the left and right sides of a test using a relational operator. Using these precedence rules, the expression above is evaluated as follows:

	3 + 2 * 2 == 9
	3 + 4 == 9
	7 == 9
	false
You can put arbitrary expressions on either side of the relational operator as long as they are of a compatible type. Here is a test with complex expressions on either side:

	(2 - 3 * 8) /  (435 % (7 * 2)) <= 3.8 - 4.5 / (2.2 * 3.8)

4.4.1 A Sample Program With Ifs

Let's put together what we have learned about the Scanner class with some simple if statements. Suppose that we want to read a sequence of numbers and compute the average. We can ask the user how many numbers to work with and use a for loop to prompt for each number. If we want to compute the average, we'll have to compute the overall sum. The code we saw for cumulative sum earlier in the chapter will work nicely for that. Then we can compute the average as the sum divided by the number of numbers, as in:

    System.out.println("average = " + sum/totalNumber);
There is one minor problem with this. Suppose that when we ask the user how many numbers to process, the user says to process 0 numbers. That would mean that we never enter our cumulative sum loop and we try to compute the value of 0 divided by 0. Java would print out that the average is "NaN". This cryptic message is short for "Not a Number". It would be better to print out some other kind of message that would indicate that there weren't any numbers to average. We can use an if/else for this purpose:

    if (totalNumber <= 0)
        System.out.println("No numbers to average");
    else
        System.out.println("average = " + sum/totalNumber);
We can fit in another use of if by counting how many negative numbers are entered by the user. You will often find yourself wanting to count how many times something occurs in a program. This is easy to accomplish with an if and an integer variable called a counter. You start by initializing the counter to 0:

    int negatives = 0;
You can use any name you want for the variable. Here I used the name "negatives" because that is what we're counting. The other essential ingredient is to increment the counter inside the loop if it passes the test we're interested in:

    if (next < 0)
        negatives++;
Putting this all together and adding an introduction and user prompts, we end up with the following complete program. public class ExamineNumbers { public static void main(String[] args) { System.out.println("This program examines a sequence of numbers to"); System.out.println("find the average as well as counting how many"); System.out.println("are negative."); System.out.println(); Scanner console = new Scanner(System.in); System.out.print("How many numbers do you want me to examine? "); int totalNumber = console.nextInt(); int negatives = 0; double sum = 0.0; for (int i = 1; i <= totalNumber; i++) { System.out.print(" next number? "); double next = console.nextDouble(); sum += next; if (next < 0) negatives++; } System.out.println(); if (totalNumber <= 0) System.out.println("No numbers to average"); else System.out.println("average = " + sum/totalNumber); System.out.println("# of negatives = " + negatives); } } The program will execute something like this:

This program examines a sequence of numbers to find the average as well as counting how many are negative. How many numbers do you want me to examine? 8 next number? 2.7 next number? 3.4 next number? -19.7 next number? 208.3 next number? -42.7 next number? 8 next number? 92.3 next number? -17.2 average = 29.387500000000003 # of negatives = 3

4.4.2 Nested If/Else

Many beginners write code that looks like this:

if (<test1>) <statement1>; if (<test2>) <statement2>; if (<test3>) <statement3>; This sequential structure is appropriate if you want to execute any combination of the three statements. You might write this code in a program for a questionnaire with three optional parts, any combination of which might be applicable for a given person. Often, however, you only want to execute one of a series of statements. In such cases, it is better to nest the ifs:

if (<test1>) <statement1>; else if (<test2>) <statement2>; else if (<test3>) <statement3>; With this construct, you can be sure that one statement at most is executed. The first test to return true has its corresponding statement executed. If no tests return true, no statement is executed. If this is your objective this construct is more appropriate than the sequential if statements. It reduces the likelihood of errors and simplifies the testing process.

As you can see, nesting if statements like this leads to a lot of indentation. The indentation also isn't all that helpful because we really think of this as choosing one of a number of alternatives. K&R style has a solution for this as well. If an else is followed by an if, we put them on the same line:

if (<test1>) <statement1>; else if (<test2>) <statement2>; else if (<test3>) <statement3>; This way the various statements that we are choosing from all appear at the same level of indentation. Sun says that nested if/else statements should be indented in this way, but remember that they also always include the curly braces:

if (<test1>) { <statement1>; } else if (<test2>) { <statement2>; } else if (<test3>) { <statement3>; }

There is a variation of this structure in which the final statement is controlled by an else instead of a test.

if (<test1>) { <statement1>; } else if (<test2>) { <statement2>; } else { <statement3>; } In this construct the final branch will always be taken when the earlier tests fail and, thus, the construct will always execute exactly one of the three statements. To explore these variations, consider the task of writing out whether a number is positive, negative, or zero. You could structure this as three simple if statements as follows.

    if (number > 0)
	System.out.println("Number is positive.");
    if (number == 0)
	System.out.println("Number is zero.");
    if (number < 0)
	System.out.println("Number is negative.");
To determine how many of the printlns are potentially executed, you have to stop and think about the tests being performed. You shouldn't have to put that much effort into understanding this code. It would be more efficient and more clear to nest these if statements:

    if (number > 0)
	System.out.println("Number is positive.");
    else if (number == 0)
	System.out.println("Number is zero.");
    else if (number < 0)
	System.out.println("Number is negative.");
This solution, however, is not the best. You know that you want to execute one and only one println statement. This nested structure does not preclude the possibility of no statement being executed. If all three tests fail, no statement would be executed. With these particular tests that will never happen. If a number is neither positive nor zero, it must be negative. Thus, the final test here is unnecessary and misleading. You must think about the tests in order to know whether or not it is possible for all three of these branches to be skipped.

The best solution is the nested if/else with a final branch that is always taken if the first two tests fail:

    if (number > 0)
	System.out.println("Number is positive.");
    else if (number == 0)
	System.out.println("Number is zero.");
    else
	System.out.println("Number is negative.");
You can glance at this construct and see that exactly one println will be executed. You don't have to look at the tests being performed in order to realize this, it is a property of this kind of nested if/else. This is a good place to include a comment to make it painfully clear what is going on:

    if (number > 0)
	System.out.println("Number is positive.");
    else if (number == 0)
	System.out.println("Number is zero.");
    else  // number must be negative
	System.out.println("Number is negative.");
When you find yourself writing code to pick among alternatives like these, you have to analyze the particular problem to figure out how many of the branches you potentially want to execute. If any combination can be taken, use sequential if statements. If you want one or none of the branches to be taken, use the nested if/else with a test for each statement. If you want exactly one branch to be taken, use the nested if/else with a final branch controlled by an else rather than by a test. The table below summarizes these choices.

If/Else Options
Situation Construct Basic form
You want to execute any combination of controlled statements sequential ifs if (<test1>) <statement1>; if (<test2>) <statement2>; if (<test3>) <statement3>;
You want to execute 0 or 1 of the controlled statements nested ifs ending in test if (<test1>) <statement1>; else if (<test2>) <statement2>; else if (<test3>) <statement3>;
You want to execute exactly 1 of the controlled statements nested ifs ending in else if (<test1>) <statement1>; else if (<test2>) <statement2>; else <statement3>;

4.4.3 The Dangling Else

Nested if/else statements present a special problem called the dangling else. Suppose you are writing a loop that will examine a sequence of integer values and you want to count how many of them are negative numbers and how many are nonnegative 1-digit numbers. Assume that you don't care how many nonnegative numbers there are with more than one digit. You just want to pay attention to nonnegative 1-digit numbers and to negatives.

We can accomplish this with a nested if/else construct. Here is a pseudocode version:

	if (number is nonnegative) then
	    if (number is 1 digit) then
		increment count for nonnegative 1-digit numbers.
	else
	    increment count for negatives.
The outer if/else deals with whether or not the number is negative. Negatives should send us into the else branch where they'll be counted as negatives. The nonnegatives go into an inner if that tests whether they are 1-digit numbers.

Here is some Java code that tries to implement this pseudocode:

    if (number >= 0)
        if (number < 10)
            nonnegativeOneDigits++;
    else
        negatives++;
Unfortunately, this code doesn't work. If you test it, you will find that it doesn't count negatives properly. The problem with this code is that we have two if statements and only one else clause. Which if is this dangling else matched up with? The answer is, the closest one. In this case, the else is matched up with the inner if, which means the indentation is not correct in the code above. It should be:

    if (number >= 0)
        if (number < 10)
            nonnegativeOneDigits++;
        else
            negatives++;
This is obviously not what we intended. One good solution is to use curly braces. The curly braces effectively put parentheses around the inner if so that the else will not be matched up with it.

    if (number >= 0) {
        if (number < 10)
            nonnegativeOneDigits++;
    } else
        negatives++;
Notice that if you follow the Sun convention of always including curly braces, the problem doesn't even come up:

    if (number >= 0) {
        if (number < 10) {
            nonnegativeOneDigits++;
        }
    } else {
        negatives++;
    }
A second solution is to reverse the meaning of the outer test, allowing you to exchange the if and else parts. This solves the dangling else by putting the second if after the else.

    if (number < 0)
        negatives++;
    else if (number < 10)
        nonnegativeOneDigits++;

4.4.4 Factoring if/else Statements

Suppose you are writing a program that plays a betting game with a user and you want to give different warnings about how much cash the user has. The following nested if/else distinguishes three different cases: money less than $500, which is considered low; money between $500 and $1000, which is considered okay; and money over $1000, which is considered good. Notice that the user is given different advice in each branch:

	if (money < 500) {
	    System.out.println("You have, $" + money + " left.");
	    System.out.print("Your cash is dangerously low.  Bet carefully.");
	    System.out.print("How much do you want to bet? ");
	    bet = console.nextInt();
        } else if (money < 1000) {
	    System.out.println("You have, $" + money + " left.");
	    System.out.print("Your cash is somewhat low.  Bet moderately.");
	    System.out.print("How much do you want to bet? ");
	    bet = console.nextInt();
        } else {
	    System.out.println("You have, $" + money + " left.");
	    System.out.print("Your cash is in good shape.  Bet liberally.");
	    System.out.print("How much do you want to bet? ");
	    bet = console.nextInt();
        }
This construct is repetitious and can be reduced using a technique called factoring. With it, you factor out common pieces of code from the different branches of the if/else. The technique is simple. The above construct creates three different branches depending upon the value of the variable money. You start by writing down the series of actions being performed in each branch and comparing them.

			    Depending upon money:
					|
		+-----------------------+-----------------------+
		|			|			|
	   money < 500	       500 <= money < 1000	  Money >= 1000
		|			|			|
	Report money		Report money		Report money
	Warn about low money	Report okay money	Report good money
	Prompt for bet		Prompt for bet		Prompt for bet
	Read bet		Read bet		Read bet
You can factor at both the top and the bottom of such a construct. If you notice that the top statement in each branch is the same, you factor it out of the branching part and put it before the branch. Similarly, if the bottom statement in each branch is the same, you factor it out of the branching part and put it after the loop. You can factor the top statement in each of these branches and the bottom two statements.

				Report money

			    Depending upon money:
					|
		+-----------------------+-----------------------+
		|			|			|
	   money < 500	       500 <= money < 1000	  Money >= 1000
		|			|			|
	Warn about low money	Report okay money	Report good money

Prompt for bet Read bet

Thus, the code above can be reduced to the following which is more succinct.

	System.out.println("You have, $" + money + " left.");
	if (money < 500)
	    System.out.print("Your cash is dangerously low.  Bet carefully.");
        else if (money < 1000)
	    System.out.print("Your cash is somewhat low.  Bet moderately.");
        else
	    System.out.print("Your cash is in good shape.  Bet liberally.");
	System.out.print("How much do you want to bet? ");
	bet = console.nextInt();

4.5 Testing Before Reading

The Scanner class has methods that allow you to perform a test before you read something. In other words, it allows you to look before you leap. For each of the "next" methods of the Scanner class, there is a corresponding "has" method that tells you whether or not you can perform the given operation.

For example, we will often want to read an int using a Scanner object. But what if the user types something other than an int? Scanner lets you test this. You would call the method nextInt() to read the int. Scanner has a corresponding method hasNextInt() that tells you whether or not reading an int is possible right now. To answer the question, the Scanner object looks at the next token and sees if it can be interpreted as an integer.

This can be confusing at first because we tend to think of certain sequences of characters as being one and only one kind of thing. But when we read tokens, they can be interpreted in different ways. The following program will allow us to explore this.

public class ExamineInput1 { public static void main(String[] args) { System.out.println("This program examines a token and tells you"); System.out.println("the ways in which it could be read."); System.out.println(); Scanner console = new Scanner(System.in); System.out.print("token? "); System.out.println(" hasNextInt = " + console.hasNextInt()); System.out.println(" hasNextDouble = " + console.hasNextDouble()); System.out.println(" hasNextBoolean = " + console.hasNextBoolean()); System.out.println(" hasNext = " + console.hasNext()); } } Here is what happens when we enter the token "348":
This program examines a token and tells you
the ways in which it could be read.

token? 348
  hasNextInt = true
  hasNextDouble = true
  hasNextBoolean  = false
  hasNext = true
As you'd expect, the call on hasNextInt() returns true, which means that we could interpret this token as an integer. But the scanner would also allow us to interpret this token as a double, which is why hasNextDouble() also returns true. The scanner would not be able to interpret this as a boolean value, but notice that hasNext() also returns true. That means that we could call the next() method to read this in as a String.

Here's another execution, this time for the token "348.2":

This program examines a token and tells you
the ways in which it could be read.

token? 348.2
  hasNextInt = false
  hasNextDouble = true
  hasNextBoolean  = false
  hasNext = true
The scanner can't interpret this as an int or a boolean, but it can interpret it as a double or a String. Here is an execution for the token "true":

This program examines a token and tells you
the ways in which it could be read.

token? true
  hasNextInt = false
  hasNextDouble = false
  hasNextBoolean  = true
  hasNext = true
It can't be interpreted as an int or a double, but it can be interpreted as a boolean or as a String. Finally, consider this execution for the token "hello":

This program examines a token and tells you
the ways in which it could be read.

token? hello
  hasNextInt = false
  hasNextDouble = false
  hasNextBoolean  = false
  hasNext = true
It can't be interpreted as an int, double or boolean. It can only be interpreted as a String.

Below is a program that carries this a bit further, using the various "has" methods to test a series of tokens typed by the user and to read them into a variable of an appropriate type. A nested if/else structure is used to create a hierarchy of inputs. If the input can be interpreted as an integer, we read it as an integer. If not, we see if we can read it as a double. If not, we see if we can read it as a boolean. If not, we read it as a String.

public class ExamineInput { public static void main(String[] args) { System.out.println("This program examines a sequence of tokens and"); System.out.println("tells you what kind of token it is."); System.out.println(); Scanner console = new Scanner(System.in); System.out.print("How many tokens do you want me to examine? "); int totalNumber = console.nextInt(); for (int i = 1; i <= totalNumber; i++) { System.out.print(" next token? "); if (console.hasNextInt()) { int next = console.nextInt(); System.out.println(" token is an int with value " + next); } else if (console.hasNextDouble()) { double next = console.nextDouble(); System.out.println(" token is a double with value " + next); } else if (console.hasNextBoolean()) { boolean next = console.nextBoolean(); System.out.println(" token is a boolean with value " + next); } else { String next = console.next(); System.out.println(" token is a word with value " + next); } } } } The program will execute something like this:

This program examines a sequence of tokens and tells you what kind of token it is. How many tokens do you want me to examine? 7 next token? 384 token is an int with value 384 next token? -12 token is an int with value -12 next token? 219.4 token is a double with value 219.4 next token? 78. token is a double with value 78.0 next token? 3.4.5 token is a word with value 3.4.5 next token? hello-there token is a word with value hello-there next token? true token is a boolean with value true

4.6 Exceptions

At the beginning of the chapter we described the idea of an exception and mentioned that Scanner objects can "throw" an exception if they are asked to do something unreasonable. In particular, if you call the nextInt() method of a Scanner object and it finds something that is not an integer, then it throws an exception.

In a later chapter we will see how to handle exceptions. For now we just want to explore some of the ways in which exceptions can occur and how we might want to generate them in our own code. Ideally programs execute without generating any errors, but in practice various problems arise. You might ask the user for an integer and the user will accidentally or perhaps even maliciously type something that is not an integer. Or you might execute code that has a bug in it.

A common exception that occurs is the "null pointer exception". This happens when you fail to initialize a variable that is supposed to refer to an object. For example, suppose that you want to manipulate an ArrayList object and you intend to say something like this:

    ArrayList list = new ArrayList();
    list.add("hello");
but instead you accidentally say:

    ArrayList list = null;
    list.add("hello");
Remember that the value null means "no object". So our ArrayList variable refers to nothing when we meant to have it refer to an actual ArrayList. What is Java supposed to do when we try to add something to a nonexistent list? It could ignore the error and continue executing, but that may not be the right thing to do. Instead Java throws an exception that stops the program from executing and gives us a warning that a null pointer exception occurred while executing this line of code.

We may want to throw exceptions ourselves in the code we write. For example, suppose that we want to write a method for computing the factorial of an integer. The factorial is defined as follows.

	n! (which is read as "n factorial") = 1 * 2 * 3 * ... * n
We can write a Java method that uses a cumulative product to compute this result:

    public static int factorial(int n) {
	int product = 1;
	for (int i = 2; i <= n; i++)
	    product *= i;
	return product;
    }
We could test the method for various values with a loop:

	for (int i = 0; i <= 10; i++)
	    System.out.println(i + "! = " + factorial(i));
which produces the following output: 0! = 1 1! = 1 2! = 2 3! = 6 4! = 24 5! = 120 6! = 720 7! = 5040 8! = 40320 9! = 362880 10! = 3628800 It seems odd that the factorial method should return 1 when you ask for 0!, but that is actually part of the mathematical definition of the factorial function. It is returning 1 because the local variable product in the factorial method is initialized to 1 and we never enter the loop when the parameter n has the value 0. So this is actually desirable behavior for 0!.

But what if we are asked to compute the factorial of a negative number? The method returns the same value of 1. The mathematical definition of factorial says that the function is undefined for negative values of n. So we shouldn't even compute an answer when n is negative. We could and should include a comment about this restriction, but what if someone calls our factorial method with a negative value anyway? This is a good place to throw an exception and Java makes it easy. Java has a class called IllegalArgumentException that is meant to cover just this case. So all we have to do is to construct an object of that type and "throw" it. Java has a special keyword "throw" that is used to throw an exception.

So at the beginning of the factorial method we introduce a test on the value of n and throw an exception if n is negative:

    if (n < 0)
        throw new IllegalArgumentException();
You can also include some text when you construct the exception that will be displayed when the exception is thrown:

    if (n < 0)
        throw new IllegalArgumentException("negative value in factorial");
Incorporating these lines of code into our definition of the method, we obtain the following:

    public static int factorial(int n) {
        if (n < 0)
            throw new IllegalArgumentException("negative value in factorial");
	int product = 1;
	for (int i = 2; i <= n; i++)
	    product *= i;
	return product;
    }
We can test this with the following main method:

    public static void main(String[] args) {
	System.out.println(factorial(-1));
    }
When you execute this program, it stops executing with the following message:

    Exception in thread "main" java.lang.IllegalArgumentException: negative
    value in factorial
          at Factorial2.factorial(Factorial2.java:8)
          at Factorial2.main(Factorial2.java:3)
The message indicates that the program Factorial2 stopped running because an IllegalArgumentException was thrown. The system then shows you a backward trace of how it got there. It was in line 8 of the factorial method of the Factorial2 class. It got there because of a call in line 3 of method main of the Factorial2 class. This kind of information is very helpful in figuring out where the bugs are in your programs.

This is an example of "defensive programming." We don't intend to have bugs in the programs we write, but we're only human, so we want to build in mechanisms that will give us feedback when we make mistakes. Testing the values passed to methods and throwing IllegalArgumentException when a value is not appropriate is a great way to provide that feedback.

4.7 Programming Problems

  1. Write a program that prompts for an integer and reports the factors of the integer (i.e., the numbers that go evenly into it).

  2. Write a program that prompts for the lengths of the sides of a triangle and reports the three angles.

  3. Write a program that prompts for a number and displays it in Roman numerals.

  4. Write a program that prompts for a date (month, day, year) and reports the day of the week for that date. It might be helpful to know that January 1st, 1601 was a Monday.

  5. A new tax law has just been passed by the government: the first $3,000 of income is free of tax, the next $5,000 is taxed at 10%, the next $20,000 is taxed at 20%, and the rest is taxed at 30%. Write an interactive program that prompts for a user's income and reports the corresponding tax.

  6. A useful technique for catching typing errors is to use a check-digit. For example, suppose that a school assigns a six-digit number to each student. A seventh digit can be determined from the other digits, as in:

    	(1 * (1st digit) + 2 * (2nd digit) + ... + 6 * (6th digit)) % 10
          
    When someone types in a student number, they type all seven digits. If the number is typed incorrectly, the check-digit will fail to match in 90% of the cases. Write an interactive program that prompts for a six-digit student number and reports the check digit for that number using the scheme described above.


Stuart Reges
Last modified: Tue Oct 26 15:31:16 PDT 2004