---
# System prepended metadata

title: 'Jam 07 - Exercise 2: Exception Handling'
tags: [polymorphism, "2 \U0001F4DD in writing", inheritance, uml, interfaces, junit, abstract-classes, "3 \U0001F9EA in testing", "4 \U0001F973 done"]

---

---
title: "Jam 07 - Exercise 2: Exception Handling"
tags: 
  - 2 📝 in writing
  - 3 🧪 in testing
  - 4 🥳 done
  - inheritance
  - interfaces
  - polymorphism
  - uml
  - abstract-classes
  - junit
---
<!-- 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 - Exception Handling

In this exercise, you'll learn about exception handling in Java and how to use it to manage error conditions in your code. You'll create custom exceptions and update the search functionality to properly handle error cases.

:::info
🔑 **Key Concepts**

- Exception handling fundamentals
- Custom exception classes
- Try-catch blocks
- Exception propagation
- Error handling best practices

:::

## The Problem - Exercise 2

Our search functionality needs better error handling. Currently, it silently fails or returns empty results when something goes wrong. We should:

1. Create custom exceptions for search errors
2. Update the Searchable interface to handle exceptions
3. Modify implementations to throw appropriate exceptions
4. Add proper error handling in search methods
5. Write tests to verify exception behavior

This will make our code more robust and provide better feedback when things go wrong.

## Understanding Exceptions

Exceptions are Java's way of handling error conditions. Key concepts include:

- **Exception Types**
    - Checked exceptions (must be handled)
    - Unchecked exceptions (runtime errors)
    - Custom exceptions (application-specific errors)

- **Exception Handling**
    - Try-catch blocks
    - Throws clauses
    - Exception propagation
    - Finally blocks

### Example: Custom Exceptions

Let's look at an example of a custom exception and how it's used:

```java
// Example: A simple custom exception
public class SearchFailedException extends Exception {
    public SearchFailedException() {
        // Default string
        super("Search operation failed");
    }

    public SearchFailedException(String message) {
        super(message);
    }
}


// Example: A custom exception for insufficient funds that stores parameters
// This is useful for throwing information up the call stack that might not otherwise be available
public class InsufficientFundsException extends Exception {
    private final int required;
    private final int available;
    
    public InsufficientFundsException(int required, int available) {
        super(String.format("Required: $%d, Available: $%d", required, available));
        this.required = required;
        this.available = available;
    }
    
    public int getRequired() { return required; }
    public int getAvailable() { return available; }
}
```

### Example: Using Custom Exceptions

Here's how this exception might be thrown and then handled (in both ways) in code:

```java
// Example 1: Throwing an exception
public void transferFunds(int amount) throws InsufficientFundsException {
    if (amount > balance) {
        throw new InsufficientFundsException(amount, balance);
    }
    balance -= amount;
}

// Example 2: Handling exceptions
public void processPayment(int amount) {
    try {
        if (amount > balance) {
            throw new InsufficientFundsException(amount, balance);
        }
        balance -= amount;
    } catch (InsufficientFundsException e) {
        System.out.println("Payment failed: " + e.getMessage());
        // Handle the error appropriately
    }
}

// Example 3: Propagating exceptions
public void withdrawFunds(int amount) throws InsufficientFundsException {
    if (amount > balance) {
        throw new InsufficientFundsException(amount, balance);
    }
    balance -= amount;
}
```

### Example: Testing Exceptions

Here's how we might test code that throws exceptions. When testing exceptions, it's important to understand the flow:

1. First, the code after the `() ->` (like creating a new Thermostat in the example below) is executed.  We will learn more about this `() ->` syntax later!
2. If that code throws an exception, assertThrows catches it
3. assertThrows then verifies that the caught exception is of the expected type

Here's an example:

```java
@Test
void shouldThrowInvalidTemperatureException() {
    // -500°C is below absolute zero (-273.15°C), which is physically impossible
    // We're testing that creating a Thermostat with this invalid temperature
    // throws our custom exception
    assertThrows(jam12.InvalidTemperatureException.class, () ->
        new Thermostat(-500));
}
```

## Your Tasks

Now that you understand how custom exceptions work, you'll:

1. Create a custom `InvalidPriceException` in your `jam07` package for invalid product prices
2. Update the `Product` class to use this exception
    - hint 1: if the code is DRY, there is one place to throw this, otherwise there will be two places
    - hint 2: there will be two methods that need `throws` added to their method signature
    - hint 3: the class needs to throw the exception also!
3. Modify your implementations to throw or handle exceptions as appropriate.  An Example of one way to handle this is below.
   ![ExampleChangeToRegisterDriver](https://www.dropbox.com/scl/fi/t7uanj9cdizmyrdqxawbo/ExampleChangeToRegisterDriver.png?rlkey=ova0mwl8uct10rqk30qaqrgq0&raw=1)
4. Modify your existing tests and write any additional tests you might need to verify the exception behavior.  An Example of one way to handle this is below (note the `fail` method - this method comes from `org.junit.jupiter.api.Assertions.fail`).
    - You will come across some scope issues!  How can you resolve them?
    - You will have to change nearly every test file!
    - hint: you probably already have a `testNegativePrice` method in `jam05.ProductTest`
    - You may need to turn an edge case test into an exception test
   ![ExampleChangeToRegisterDriver](https://www.dropbox.com/scl/fi/2gyqlft8tbju99zg6v0wm/ExampleChangeToProductTest.png?rlkey=0nhi2r1b0o5qwi12o5b6w84g4&raw=1)

You might have noticed how one change in one file (throwing an exception) threw (no pun intended) a LOT of your other files out of wack. This is exactly why we have a robust test structure! When we make changes that affect multiple parts of our system, our tests help us find all the places that need to be updated. Without these tests, we might miss updating some files, leading to bugs in production. With our test suite, we can be confident that we've caught and fixed all the necessary changes.

:::warning
🚨 **Common Implementation Pitfalls**

- Not documenting exceptions
- Swallowing exceptions
- Using generic Exception type
- Missing try-catch blocks
- Incorrect exception propagation

:::

## Save Your Work - Exercise 2

**Run all tests to verify your implementation**:

```bash
# Right click on the test folder in IntelliJ
# Click "Run Tests in 'csci205_jams'"
# Ensure all tests pass
```

**Stage your changes**:

```bash
git add src/main/java/jam05/Product.java src/main/java/jam06/Register.java src/main/java/jam06/RegisterDriver.java src/main/java/jam07/InvalidPriceException.java src/test/java/jam05/ProductCatalogTest.java src/test/java/jam05/ProductTest.java src/test/java/jam06/TransactionTest.java src/test/java/jam07/ProductCatalogTest.java src/test/java/jam07/ProductTest.java src/test/java/jam07/RegisterTest.java src/test/java/jam07/TransactionTest.java
```

**Verify what files are uncommitted**:

Your files maybe different so the above command may have fixed something you changed so make sure you understand your `git status`.

```bash
git status
```

**Commit your work**:

```bash
git commit -m "jam07: Implement exception handling for Product and related classes"
```

:::success
🔑 **Key Takeaways**

- Exceptions handle error conditions
- Custom exceptions provide specific error types
- Try-catch blocks manage exceptions
- Exception documentation is essential
- Tests verify error handling

:::
