---
# System prepended metadata

title: Jam 04 - Exercise 2
tags: [calculator, jam04, switch, "3 \U0001F9EA in testing", control flow, "4 \U0001F973 done"]

---

---
title: "Jam 04 - Exercise 2"
tags: 
  - 3 🧪 in testing
  - 4 🥳 done
  - jam04
  - switch
  - control flow
  - calculator
---
<!-- markdownlint-disable line-length single-h1 no-inline-html -->
<!-- markdownlint-configure-file { "ul-indent": { "indent": 4 }, "link-fragments": {"ignore_case": true} } -->
{%hackmd dJZ5TulxSDKme-3fSY4Lbw %}

# Exercise 2 - Git Branching and Calculator Implementation

## Overview - Exercise 2

In this exercise, you'll learn about Git branching while implementing a calculator program. Branching is a fundamental Git concept that allows you to work on features or fixes without affecting your main codebase. This is especially important when working on teams or when you want to experiment with new features.

## Part 1 - Understanding Git Branches

Branches are a fundamental concept in Git:

- A branch is like a separate line of development
- Changes in one branch don't affect other branches
- The `main` branch typically contains stable, working code
- Feature branches (like `jam04`) are for developing new features
- Branches can be merged back together when work is complete

:::warning
🚨 **Important**: Starting with this jam, you must create a new branch to represent the work you are doing for each jam. This practice:

- Helps isolate changes
- Makes debugging easier
- Follows professional development workflows
- Protects your main branch from broken code while you're working
- Ensures your main branch stays in a gradeable state

Remember: Your main branch should always contain working code that can be graded. By working in feature branches, you can commit work-in-progress code without affecting your main branch.
:::

Before we implement our calculator, let's understand branching:

1. First, verify you're in the main branch and your working directory is clean:

   ```bash
   git status
   ```

   You should see "nothing to commit, working tree clean"

2. Create and switch to a new branch for this jam:

   ```bash
   git checkout -b jam04
   ```

   This command combines two operations:
   - `git branch jam04` - creates the new branch
   - `git checkout jam04` - switches to that branch

3. Verify you're on the new branch:

   ```bash
   git branch
   ```

   You should see:

   ```text
   * jam04
     main
   ```

   The asterisk (*) indicates your current branch.

:::info
🔧 **IntelliJ Branch Indicator**

In IntelliJ, you can always see your current branch in the top left corner of the window:

![intellij-branch-indicator](https://www.dropbox.com/scl/fi/mvvbx3jopozys047wfwkx/intelliJ-branch.png?rlkey=tb63cbferp5mea8txmkjp7d1q&raw=1)

This indicator is a quick way to verify which branch you're working on. While this is also a clickable button that provides Git operations, we'll stick to command-line Git for now. In the next jam, we'll learn how to use IntelliJ's Git integration features.

For now, just use this indicator to verify which branch you're on!
:::

## Part 2 - Calculator Implementation

Now that we're safely in our feature branch, let's implement our calculator program that demonstrates the power and convenience of Java's switch statement.

### The Calculator Goal

You're going to build a command-line calculator that processes simple arithmetic expressions. While this might seem straightforward, it's an excellent opportunity to practice several important programming concepts:

1. **Input Processing**: You'll need to parse expressions like "5.2 + 3.7" into their components
2. **Decision Making**: Different operators require different calculations
3. **Error Handling**: Users can (and will!) enter invalid input
4. **User Experience**: Clear prompts and error messages make programs more usable

This is also a perfect problem for learning about switch statements. When you have multiple operations to choose from (+, -, *, /, %, ^), you could use a chain of if-else statements, but there's a cleaner way...

### Why Switch Statements?

OK, technically, the switch statement is not necessary. And, *while* we're at it (no pun intended), nor is the for loop. And, neither is the do-while loop. Most languages really only need one construct for branching (if-else) and one for repetition (while). Those give you all the programming constructs you need!

However, modern languages give us more than that simply for convenience. The switch statement is one of those convenience constructs. It shines when you have multiple alternative branches that depend on a single value. While traditionally limited to primitive types like char, byte, short, and int, modern Java has expanded switch to support Strings and enum types, making it even more versatile.

For our calculator, where we need to branch based on the operator (+, -, *, /, %, ^), a switch statement will give us cleaner, more maintainable code than a chain of if-else statements.

:::info
🔑 **Key Concepts**

- Switch statements for clean multi-branch logic
- Input validation using Scanner
- Error handling and user feedback
- Floating-point arithmetic operations

:::

### Required Reading

Before starting this exercise, ensure you've completed the first and if you want more practice, the second:

1. **zyBooks Section 4.9** - Covers the switch statement fundamentals
2. **Java: The Complete Reference (Chapter 5)** - For a deeper understanding of switch statements
   - Available through O'Reilly Learning: [Switch Statement Section](https://learning.oreilly.com/library/view/java-the-complete/9781260463422/ch5.xhtml#ch05lev2sec2)
   - Access using your Bucknell email

### Problem Statement

Create a console-based calculator that processes simple arithmetic expressions and handles errors gracefully.

#### Input Format

- Expression format: `operand1 operator operand2`
- Operands must be floating-point numbers
- Operator must be one of:
    - Addition (+)
    - Subtraction (-)
    - Multiplication (*)
    - Division (/) *Floating-point division, not integer division*
    - Modulus (%)
    - Power (^)

#### Program Requirements

1. Process expressions until user chooses to exit
2. Validate all input before processing
3. Handle error cases including:
    - Invalid numeric input
    - Invalid operators
    - Division by zero
4. Provide clear error messages
5. Clear input buffer after reading expressions
6. Ask the user to try again after each result is printed

#### Technical Requirements/Restrictions

1. Must use switch statement for operation selection
2. Must use Scanner for all input processing
3. Must implement arithmetic operations directly (no calculator libraries)
4. Must handle floating-point numbers (not integers)
5. Must follow exact input format specified

### Getting Started with Calculator.java

Now that we understand what we need to build, let's take an iterative and incremental approach to development - a strategy commonly used in professional software development. Instead of trying to implement everything at once, we'll build our calculator through a series of small, working iterations. Each iteration will add new functionality while maintaining a working program.

Here's how we'll break down our development iterations:

1. Start with the minimal working program (single operation)
2. Add a second operation and default error case
3. Handle input buffer cleanup
4. Add program loop for multiple calculations
5. Refactor to use methods/fields
6. Figure out how to handle errors gracefully
7. Extend with remaining operations and features

This iterative approach has several benefits:

- Each step builds on a working foundation
- Easier to test and debug small changes
- Reduces development complexity
- Allows for continuous validation of your solution

Let's begin with our first iteration: a starter template that sets up the basic structure. This template includes:

- Proper package and import statements
- Basic documentation
- A Scanner for reading user input
- A greeting method to display instructions

1. Create a new file called `Calculator.java` in your `src/main/java/jam04` directory:

```java
import java.util.Scanner;

public class Calculator {
   /** Common Scanner object for reading user input */
   private static Scanner scnr;

   /**
    * Main method to display the greeting and start the calculator
    * 
    * @param args command line arguments (not used for this program)
    */
   public static void main(String[] args) {
      // Set up our Scanner object
      scnr = new Scanner(System.in);

      // Greet the user with instructions
      System.out.println("Welcome to the Calculator");
      System.out.println("\nEnter expressions with two numeric operands");
      System.out.println("and a single operator from +, -, *, /, %, or ^");
   }
}
```

### Implementation Steps

#### Step 1 - Starting Small

Let's begin with just the addition operator.  We're not going to deal with error handling yet.  So, let's assume the user will always input valid expressions (for now!).

Add this code below the greeting in main:

```java
// Use Scanner to parse our input
double num1 = scnr.nextDouble();
String operator = scnr.next();
double num2 = scnr.nextDouble();
```

Now, let's add the switch statement to handle the addition operator:

```java
// Handle the addition operator
switch (operator) {
case "+":
    System.out.println("The sum is " + (num1 + num2));
    break;
}
```

Run and test your program with a simple addition (like "2 + 3"). Remember, good software engineers focus on iterative development and test often!

```text
Welcome to the Calculator

Enter expressions with two numeric operands
and a single operator from +, -, *, /, %, or ^
2 + 3
The sum is 5.0

BUILD SUCCESSFUL in 7s
```

You may notice that checkstyle is complaining about a few things in the switch statement.  You can ignore these for now.  We'll deal with them in a few steps.

#### Step 2 - Adding More Operations

Nice! You already have the basic structure of the program working. Now, let's add subtraction and a default case to handle unsupported operators.  Update your switch statement to look like this:

```java
switch (operator) {
case "+":
    System.out.println("The sum is " + (num1 + num2));
    break;
case "-":
    System.out.println("The difference is " + (num1 - num2));
    break;
default:
    System.out.println("Calculator Error: " + operator + " is not a valid operator.");
    break;
}
```

That fixes one of the checkstyle errors!

Test it with both supported and unsupported operators to see how the default case works.

#### Step 3 - Handling Input Buffer

When using Scanner, it's important to understand how it handles input. The Scanner reads input tokens (like numbers and words) but leaves the newline character (`\n`) in the input buffer. This can cause problems when mixing different types of input methods (like `nextDouble()` and `nextLine()`).

For example:

1. When you type "2 + 3" and press ENTER
2. `nextDouble()` reads "2"
3. `next()` reads "+"
4. `nextDouble()` reads "3"
5. But the ENTER key's newline character is still in the buffer!

If we don't clear this leftover newline, it can cause problems when we try to read the next line of input (like when asking "Try again?"). Add this line after reading num2:

```java
scnr.nextLine();  // Clear the input buffer
```

This is a common source of bugs when working with Scanner, so it's important to understand and handle properly.

Run your program again, testing both valid and invalid expressions to make sure your program still works the way you expect it.  This is an imporant part of iterative and incremental development.  Change a thing, run it, see if it works, and then change another thing.

#### Step 4 - Adding the Loop

Let's make our calculator continue until the user wants to stop. Add a do-while loop after your initial greeting:

```java
// Helper variable to track if we're done
boolean isDone = false;

// Loop until the user is done
do {
    System.out.print("\nEnter a simple arithmetic expression with spacing: ");
    
    // Your existing input and switch code goes here
    
    // Ask if user wants to continue
    System.out.print("\nTry again? [y | n] ");
    isDone = scnr.nextLine().strip().equalsIgnoreCase("n"); // this could also be written as an if statement
} while (!isDone);
```

Notice the changes that were made:
    1. A new `boolean` variable called `isDone`, initialized to `false`, since we're not done!
    2. `do {`
    3. An additional print statement to ask for an expression
    4. At the bottom, a print statement to ask the user if they want to try again
    5. A `nextLine().strip().equalsIgnoreCase()` call that checks to see if the user entered "n". If so, we set isDone to **true**.You absolutely must seek to understand code that is given to you! This line of code is doing a LOT on one single line! You better understand it before continuing!
        - Could you have written it as an if statement?  Yes, you could!
        - Is it better in one line? Is it better as an if statement? This is another one of those debates!
    6. `} while (!isDone);`

This step added a LOT of code! So, you better test it out again!

```text
Welcome to the Calculator

Enter expressions with two numeric operands
and a single operator from +, -, *, /, %, or ^

Enter a simple arithmetic expression with spacing: 2 + 3
The sum is 5.0

Try again? [y | n] y

Enter a simple arithmetic expression with spacing: 2 - 3
The difference is -1.0

Try again? [y | n] n

BUILD SUCCESSFUL in 12s
```

If your program isn't working as expected, you can use the debugger we learned about in the previous jam to step through the code and see where it's going wrong.  A common problem at this point is that the program asked "Try again?" but then goes right back to the top of the loop without giving the user a chance to answer Yes or No.  If this is happening to you, you missed a step!

#### Step 5 - Refactoring to Use Methods/Fields

Let's refactor the program to use methods and fields.  This will help us clean up the code and make it more maintainable.  It's a great idea to do this early now that we have a good base of a working program.

:::info
🔧 **Refactoring**

Refactoring is the process of improving the structure of existing code without changing its external behavior. It's a crucial skill for creating clean, maintainable code.

This is a common interview question!  It's a great way to show that you understand code and how to make it better.  We will stress refactoring as a key skill in this course!
:::

1. Use what you learned in the previous jam to extract a method called `displayGreeting()`.  Don't forget to add a JavaDoc comment!
2. Let's move our variables to fields. In your main method, you should have three variables we use throughout the program: `num1`, `operator`, and `num2`. Let's refactor these to be class fields:

    a. Right click on the `num1` variable declaration
    b. Select "Refactor" → "Introduce Field"
    c. Press enter to leave the name as is
    d. Repeat for `operator` and `num2`
    ![extract-field](https://www.dropbox.com/scl/fi/pujxyeawu1xip17lqgwkb/refactor-field.gif?rlkey=r6qf4reh7t0dqw53ql9eox009&raw=1)

3. After refactoring, your class should now have these fields at the top:

![refactor-result](https://www.dropbox.com/scl/fi/fqloxnqf0fvj24qr8yff1/refactor-result.png?rlkey=6s0kx4fsi1cxx7o4p6xobk374&raw=1)

4. Run your program to verify it still works exactly the same. Remember, refactoring should not change the program's behavior!

##### Detour: Why Refactor to Fields?

<table>
    <tr>
        <td>
**First**, you might wonder why we are moving variables to fields.  After all, we could just keep them in the main method and even checkstyle is giving a "Field can be converted to a local variable " warning.  Good question!  Let's look at the difference:

When variables are kept in the main method:

- They are only accessible within that method
- Each new method we create needs those variables passed as parameters
- It becomes cumbersome if multiple methods need the same data

When variables are fields:

- They are accessible by all methods in the class
- We can easily extract new methods without worrying about parameter passing
- The code becomes more modular and easier to refactor

But, you might be able to see that soon we will want to refactor even more methods out and they will all need access to the same variables.  So, it's a good idea to move them to fields now.

**Second**, you might wonder why we are using the *refactoring feature* to move variables to fields.  After all, we could just move them to the top of the class declaration.  Good question!  Let's look at the difference:

- Moving variables to the top of the class declaration:
    - Is a manual process
    - Requires you to remember to do it
    - Can be error prone if you forget  
    - Requires you to find every instance of the variable in the code if you decide to change the name of the variable at the same time to make the code more readable
- Using the refactoring feature:
    - Is a quick and easy way to move variables to fields
    - Is less error prone because the refactoring feature does a good job of keeping the variables in the same order
    - Is more maintainable because the refactoring feature can even be used to move variables to fields in other classes
    - Allows you to rename the variable at the same time you move it to a field and updates all instances of the variable in the code to the new name
        </td>
    </tr>

</table>

6. Refactor the three lines that read your expression into a `readExpressionFromUser` method.  Why that name?  Because it's a method that reads an expression from the user.  It's a good idea to give your method names that are self documenting!  Nobody can really question the purpose of a method if the name is descriptive!

![readExpressionFromUser](https://www.dropbox.com/scl/fi/94ufqze3d6fruf3ei1w02/readExpressionFromUser.png?rlkey=jha4shucqja2e0oz1fiatwkom&raw=1)

#### Step 6 - Error Handling

Remember our mantra: ***"THE DEBUGGER IS MY BEST FRIEND!"*** Let's use the debugging skills we learned to understand how our calculator processes input and where we might need to add error handling.

Right now, our `readExpressionFromUser` method looks like this:

```java
private static void readExpressionFromUser() {
    // Use Scanner to parse our input
    num1 = scnr.nextDouble();
    operator = scnr.next();
    num2 = scnr.nextDouble();

    // Be sure to consume the rest of the input
    scnr.nextLine();
}
```

Let's improve this step by step:

1. First, let's add a loop to keep trying until we get good input with a boolean flag:

Before your existing code, add:

```java
private static void readExpressionFromUser() {
    // Let's assume the user does not have a good expression yet
    boolean isGoodExpression = false;
    
    while (!isGoodExpression) {
        System.out.print("\nEnter a simple arithmetic expression with spacing: ");
        
```

And after your existing code, add:

```java
        // If we made it here, we're good!
        isGoodExpression = true;
    }
}
```

Watch that you keep the right number of `}` in your code!  The code above did include some code that was already in the method (but this was given to you to provide context).

Test this with the debugger. What happens when you enter bad input like "hello + 3"?

2. Now let's read the whole line first and create a separate Scanner to parse it.  Use the following screenshot to guide what needs to be changed/added
    - left is original, right is new
    - green is new lines (there are two blocks of code to add)
    - blue is changes (there are three lines to change `scnr` to `strScanner`)
    - grey is remove (the `scnr.nextLine()` is no longer needed)

    ![readExpressionFromUser-with-second-scanner](https://www.dropbox.com/s/kvkak8mm2xvdpjc/readExpressionFromUser-with-second-scanner.png?raw=1)

    Test this version. It's better, but what happens when you enter "hello + 3" now?

3. Finally, let's add validation before we try to read the number.  Immediately before `num1 = strScnr.nextDouble();` add the following:

    ```java
            // Check if we have a valid number before trying to read it
            if (!strScanner.hasNextDouble()) {
                System.out.println("ERROR: Bad input - Try again!");
                strScanner.close();
                continue;
            }
    ```

    Pay close attention to what you are entering!  Notice how we're using a new `Scanner` object called `strScanner` to parse the input.  
    - `scnr` is reading the entire line and storing it as a string.  
    - `strScanner` is reading it token by token and storing it as the expected types.

    Notice the use of `continue`.  What this keyword does is jump back to the top of the loop and start again.  This is different than `break` which jumps out of the loop entirely.

4. Add similar validation for `num2`.

    Test this version (using the debugger) with:

    - Good input: "2 + 3"
    - Bad input: "2+3" (no spaces)
    - Bad input: "hello + 3"

Notice how each version gets more robust in handling bad input. The final version:

1. Reads the whole line first
2. Creates a separate Scanner to parse it
3. Validates input before trying to use it
4. Provides clear error messages
5. Keeps asking until we get valid input

#### Step 7 - Extending with Remaining Operations and Features

Now that you have a working foundation, you should:

1. Add the remaining operators
2. Implement error handling
3. Improve the output formatting
4. Test with various inputs

Remember to test after each change!

## Example Output

```text
Welcome to the Calculator

Enter expressions with two numeric operands
and a single operator from +, -, *, /, %, or ^

Enter a simple arithmetic expression with spacing: 2f + 2
ERROR: Bad input - Try again!

Enter a simple arithmetic expression with spacing: 2 & 3
Calculator Error: & is not a valid operator.

Try again? [y | n] y

Enter a simple arithmetic expression with spacing: 2 + 2f
ERROR: Bad input - Try again!

Enter a simple arithmetic expression with spacing: 20.5 + 0.5
The sum is 21

Try again? [y | n] y

Enter a simple arithmetic expression with spacing: 20.5 - 19.25
The difference is 1.25

Try again? [y | n] y

Enter a simple arithmetic expression with spacing: 10 * 100
The product is 1,000

Try again? [y | n] y

Enter a simple arithmetic expression with spacing: 100 / 0.25
The quotient is 400

Try again? [y | n] y

Enter a simple arithmetic expression with spacing: 10 ^ 10
The power is 10,000,000,000

Try again? [y | n] y

Enter a simple arithmetic expression with spacing: 17 % 3
The modulo is 2

Try again? [y | n] n

BUILD SUCCESSFUL in 50s
2 actionable tasks: 1 executed, 1 up-to-date
```

🔧 **Enhancing Output with Java's Number Formatting**

Want to make your calculator's output look more professional? Java provides powerful internationalization support through the `DecimalFormat` class, which lets you:

1. Add thousands separators (commas) for large numbers
2. Control decimal places for fractional results
3. Format numbers according to different regional standards

Notice in the example output above how numbers like "1,000" and "10,000,000,000" have commas for readability, while decimal results like "1.25" show just the necessary decimal places. This clean formatting was achieved using Java's `DecimalFormat` class.

For implementation details and pattern options, check out:

- [DecimalFormat API](https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/text/DecimalFormat.html) for full documentation
- [Java i18n Tutorial](https://dev.java/learn/numbers-strings/numbers/#decimalformat) for more examples and patterns

> 🔍 **Checkpoint**
>
> - Calculator.java is created and compiles without errors
> - Program correctly handles all six operations
> - Invalid input is caught and appropriate error messages displayed
> - Program continues until user chooses to exit

## Save Your Work - Exercise 2

Verify what files are uncommitted:

```bash
git status
```

Stage your changes:

```bash
git add src/main/java/jam04/Calculator.java
```

Commit your work:

```bash
git commit -m "jam04: Implement calculator with switch statement"
```

Your working directory should now be clean.

:::success
🔑 **Key Takeaways**

- Switch statements provide clean, maintainable code for multiple branching logic
- Input validation is crucial for robust program behavior
- Exception handling helps manage error cases gracefully
- Scanner can be used flexibly for both console and String parsing

:::
