[JAVA-2i] Building a Simple Calculator: Using Recursion to Gracefully Handle User Mistakes

Jack Boyuan Xu
5 min readJan 26, 2020

--

Enough talk about concepts, it’s time to get back to our calculator and give it a nice polish. Until now, we built our calculator largely with one assumption: the user won’t make a mistake and enter something they shouldn’t. It isn’t a fair assumption. Just because I personally never make mistakes doesn’t mean other people never make mistakes… Making false assumptions about others is not a good thing to do.

Recall the current behavior of our program when the user enters something they shouldn’t:

if (arithmeticChoice == 1) {
result = number1 + number2;
} else if (arithmeticChoice == 2) {
result = number1 - number2;
} else if (arithmeticChoice == 3) {
result = number1 * number2;
} else if (arithmeticChoice == 4) {
result = number1 / number2;
} else {
System.out.println("Invalid arithmetic choice! Aborting…");
System.exit(7);
}

Our program abruptly ends without giving the user another chance. Our goal is to make it so that the user is given another chance if they mess up the input. We can do so with some code refactoring and recursion.

Recursion: The Definition

“The definition of recursion is the definition of recursion.”

Recursion is a technique where a method calls itself until some condition is met. If there aren’t conditions breaking the method out of this self-calling cycle, your program will eventually run out of memory and hard crash. Here’s an example to demonstrate recursion:

We define a new arithmetic operation called ?!?! where, for example, 5?!?! = 5+4+3+2+1 = 15. How can we generalize this for any positive integer x?

Let’s dissect this question. What we want here is to decrement the input number by 1 and keep adding those numbers until we reach 1. We can translate this logic into the following pseudocode:

Solve ?!?! {
If the number is 1:
return 1, because we stop at 1
Else:
subtract the number by 1 and solve again {
If the number is 1:
return 1, because we stop at 1
Else:
subtract the number by 1 and solve again {
If the number is 1:
return 1, because we stop at 1
Else:
subtract the number by 1 and solve again {

}
}
}
}

We can see a pattern here where the code keeps repeating itself. We can simplify the above into:

Solve ?!?! {
If the number is 1:
return 1, because we stop at 1
Else:
subtract the number by 1 and solve again {
Solve ?!?!
}
}

And there, my friends, is our recursion. The case where the number equals 1 is called the base case. The base case is the brakes on the recursion. Without it, the recursion will go on forever until the program runs out of memory. Let’s transform the above into code (note that this will need to be in a class):

public int solve(int number) {
if (number == 1) {
return 1;
} else {
return solve(number-1) + number;
}
}

Recursion: In Our Simple Calculator

What we will do is much simpler than the recursion above. Here’s the plan (pseudocode) :

Ask for user arithmetic choice () {
if (choice is not valid) {
print error message
return Ask for user arithmetic choice ()
}
return choice
}

However, currently we have our Ask for user arithmetic choice () process embedded in the main method:

We have to extract the highlighted lines into a new method so we can utilize recursion, like so:

There are some compile-time issues with the code at this moment:

  1. userInput is not accessible anymore within the scope of our new method.
  2. Scanner is outside the scope of our new method as well.
  3. On line 51, the variable arithmeticChoice is out of the scope of the main method.
  4. Our new method, which is supposed to return an integer, has no return value.

We also need to check if the user input is valid.

Let’s go ahead and fix them!

Issue 1: userInput is not accessible anymore within the scope of our new method.

This one is simple. We can simply re-declare the variable within the scope of our function by replacing line 35 with:

String userInput = s.nextLine();

Issue 2: Scanner is outside the scope of our new method as well.

We can provide a bandaid fix for this one by moving its declaration outside the main method (yes, this is valid):

Scanner is now declared on line 7.

Why is it a bandaid fix? Don’t worry about it for now. If you’re curious, read more about it in my other article.

Issue 3: On line 51, the variable arithmeticChoice is out of the scope of the main method.

We can fix this just like how we fixed issue 1 by declaring the variable in the main method again. But what’s its value? Recall our new method has a return type of integer that returns the choice the user makes, so let’s make use of it:

Issue 4: Our new method, which is supposed to return an integer, has no return value.

Let’s hold off on this fix and talk about it in the next section.

Checking the Validity of User Input

We only have four options from 1 to 4 so we want to check if the user input is between 1 and 4. We can do something like this:

if (arithmeticChoice >= 1 AND arithmeticChoice <= 4) {
// valid
} else {
// invalid
}

Recall the pseudocode we wrote earlier:

Ask for user arithmetic choice () {
if (choice is not valid) {
print error message
return Ask for user arithmetic choice ()
}
return choice
}

So we can transform our existing code into:

And now we are done! Try it and give it a number outside of the range and we will see the program prompt us again for a correct input:

However, if we give it something other than a number…

The program crashes because it is only expecting a number. We will fix it using “try-catch” blocks in the next article.

--

--

Jack Boyuan Xu

Co-founder & Tech Lead @ EthSign. Blockchain Lecturer @ USC.