Errors in Mercury

Our error handling example demonstrates how to work with errors in Java. This approach differs from Go’s explicit error returns, but we’ll show similar concepts using Java’s exception handling mechanism.

import java.util.Arrays;

public class ErrorHandling {

    // Custom exceptions
    static class TeaException extends Exception {
        public TeaException(String message) {
            super(message);
        }
    }

    static class OutOfTeaException extends TeaException {
        public OutOfTeaException() {
            super("no more tea available");
        }
    }

    static class PowerException extends TeaException {
        public PowerException() {
            super("can't boil water");
        }
    }

    // Method that may throw an exception
    public static int f(int arg) throws Exception {
        if (arg == 42) {
            throw new Exception("can't work with 42");
        }
        return arg + 3;
    }

    // Method that may throw custom exceptions
    public static void makeTea(int arg) throws TeaException {
        if (arg == 2) {
            throw new OutOfTeaException();
        } else if (arg == 4) {
            // We can wrap exceptions to add context
            throw new TeaException("making tea: " + new PowerException().getMessage());
        }
    }

    public static void main(String[] args) {
        for (int i : Arrays.asList(7, 42)) {
            try {
                int r = f(i);
                System.out.println("f worked: " + r);
            } catch (Exception e) {
                System.out.println("f failed: " + e.getMessage());
            }
        }

        for (int i = 0; i < 5; i++) {
            try {
                makeTea(i);
                System.out.println("Tea is ready!");
            } catch (TeaException e) {
                if (e instanceof OutOfTeaException) {
                    System.out.println("We should buy new tea!");
                } else if (e.getMessage().contains("can't boil water")) {
                    System.out.println("Now it is dark.");
                } else {
                    System.out.println("unknown error: " + e.getMessage());
                }
            }
        }
    }
}

In Java, we use exceptions to handle errors. Here’s a breakdown of the key concepts:

  1. We define custom exceptions (TeaException, OutOfTeaException, PowerException) to represent specific error conditions.

  2. Methods that can produce errors declare the exceptions they might throw using the throws keyword.

  3. We use try-catch blocks to handle exceptions. This is similar to checking for non-nil errors in Go.

  4. Instead of returning multiple values (result and error), Java methods either return a value or throw an exception.

  5. We can create exception hierarchies (e.g., OutOfTeaException extends TeaException) to represent different levels of errors.

  6. To check for specific types of exceptions, we use instanceof or examine the exception message. This is similar to using errors.Is in Go.

  7. We can wrap exceptions by including the message of one exception in another, which is similar to error wrapping in Go.

When you run this program, you’ll see output similar to:

f worked: 10
f failed: can't work with 42
Tea is ready!
Tea is ready!
We should buy new tea!
Tea is ready!
Now it is dark.

This example demonstrates how to handle errors, create custom exceptions, and check for specific error types in Java. While the syntax and mechanisms differ from Go, the core concepts of error handling and propagation remain similar.