Creating Custom Exceptions

You can’t throw built-in Apex exceptions but can only catch them. With custom exceptions, you can throw and catch them in your methods. Custom exceptions enable you to specify detailed error messages and have more custom error handling in your catch blocks.

Exceptions can be top-level classes, that is, they can have member variables, methods and constructors, they can implement interfaces, and so on.

To create your custom exception class, extend the built-in Exception class and make sure your class name ends with the word Exception, such as “MyException” or “PurchaseException”. All exception classes extend the system-defined base class Exception, and therefore, inherits all common Exception methods.

This example defines a custom exception called MyException.
public class MyException extends Exception {}

Like Java classes, user-defined exception types can form an inheritance tree, and catch blocks can catch any object in this inheritance tree. For example:

public class BaseException extends Exception {}
public class OtherException extends BaseException {}

try {
    Integer i;
    // Your code here
    if (i < 5) throw new OtherException('This is bad');
} catch (BaseException e) {  
    // This catches the OtherException
}

Here are some ways you can create your exceptions objects, which you can then throw.

You can construct exceptions:
  • With no arguments:
    new MyException();
  • With a single String argument that specifies the error message:
    new MyException('This is bad');
  • With a single Exception argument that specifies the cause and that displays in any stack trace:
    new MyException(e);
  • With both a String error message and a chained exception cause that displays in any stack trace:
    new MyException('This is bad', e);

Rethrowing Exceptions and Inner Exceptions

After catching an exception in a catch block, you have the option to rethrow the caught exception variable. This is useful if your method is called by another method and you want to delegate the handling of the exception to the caller method. You can rethrow the caught exception as an inner exception in your custom exception and have the main method catch your custom exception type.

The following example shows how to rethrow an exception as an inner exception. The example defines two custom exceptions, My1Exception and My2Exception, and generates a stack trace with information about both.

// Define two custom exceptions
public class My1Exception extends Exception {} 
public class My2Exception extends Exception {} 

try { 
    // Throw first exception
    throw new My1Exception('First exception'); 
} catch (My1Exception e) { 
    // Throw second exception with the first 
    // exception variable as the inner exception
    throw new My2Exception('Thrown with inner exception', e);
} 

This is how the stack trace looks like resulting from running the code above:

15:52:21:073 EXCEPTION_THROWN [7]|My1Exception: First exception

15:52:21:077 EXCEPTION_THROWN [11]|My2Exception: Throw with inner exception

15:52:21:000 FATAL_ERROR AnonymousBlock: line 11, column 1

15:52:21:000 FATAL_ERROR Caused by

15:52:21:000 FATAL_ERROR AnonymousBlock: line 7, column 1

The example in the next section shows how to handle an exception with an inner exception by calling the getCause method.

Inner Exception Example

Now that you’ve seen how to create a custom exception class and how to construct your exception objects, let’s create and run an example that demonstrates the usefulness of custom exceptions.

  1. In the Developer Console, create a class named MerchandiseException and add the following to it:
    public class MerchandiseException extends Exception {}

    You’ll use this exception class in the second class that you’ll create. Note that the curly braces at the end enclose the body of your exception class, which we left empty because we get some free code—our class inherits all the constructors and common exception methods, such as getMessage, from the built-in Exception class.

  2. Next, create a second class named MerchandiseUtility.
    public class MerchandiseUtility {
        public static void mainProcessing() {
            try {
                insertMerchandise();
            } catch(MerchandiseException me) {
                System.debug('Message: ' + me.getMessage());    
                System.debug('Cause: ' + me.getCause());    
                System.debug('Line number: ' + me.getLineNumber());    
                System.debug('Stack trace: ' + me.getStackTraceString());    
            }
        }
        
        public static void insertMerchandise() {
            try {
                // Insert merchandise without required fields
                Merchandise__c m = new Merchandise__c();
                insert m;
            } catch(DmlException e) {
                // Something happened that prevents the insertion
                // of Employee custom objects, so throw a more
                // specific exception.
                throw new MerchandiseException(
                    'Merchandise item could not be inserted.', e);
            }
        }
    }

    This class contains the mainProcessing method, which calls insertMerchandise. The latter causes an exception by inserting a Merchandise without required fields. The catch block catches this exception and throws a new exception, the custom MerchandiseException you created earlier. Notice that we called a constructor for the exception that takes two arguments: the error message, and the original exception object. You might wonder why we are passing the original exception? Because it is useful information—when the MerchandiseException gets caught in the first method, mainProcessing, the original exception (referred to as an inner exception) is really the cause of this exception because it occurred before the MerchandiseException.

  3. Now let’s see all this in action to understand better. Execute the following:
    MerchandiseUtility.mainProcessing();
  4. Check the debug log output. You should see something similar to the following:

    18:12:34:928 USER_DEBUG [6]|DEBUG|Message: Merchandise item could not be inserted.

    18:12:34:929 USER_DEBUG [7]|DEBUG|Cause: System.DmlException: Insert failed. First exception on row 0; first error: REQUIRED_FIELD_MISSING, Required fields are missing: [Description, Price, Total Inventory]: [Description, Price, Total Inventory]

    18:12:34:929 USER_DEBUG [8]|DEBUG|Line number: 22

    18:12:34:930 USER_DEBUG [9]|DEBUG|Stack trace: Class.EmployeeUtilityClass.insertMerchandise: line 22, column 1

    A few items of interest:
    • The cause of MerchandiseException is the DmlException. You can see the DmlException message also that states that required fields were missing.
    • The stack trace is line 22, which is the second time an exception was thrown. It corresponds to the throw statement of MerchandiseException.
      throw new MerchandiseException('Merchandise item could not be inserted.', e);
Previous
Next