What is the best way to create a general Exception wrapper?

Viewed 109

Is it possible to have a general exception wrapper like this?

 public Function<?, Optional<?>> ExceptionWrapper = (? input) -> {
        try {
            return Optional.of(someAction(input));
        } catch (Exception e) {
            logger.warn(e.getMessage());
        }
        return Optional.empty();
    }; 

These ?s can be have different types, similar to Any in TS. The purpose is not repeating the same code.

It might be just a bad practice.

2 Answers

You can use a method like this:

public static <T> Optional<T> wrappException(SupplierWithException<T> supplierThatThrowsException) {
    try {
        return Optional.of(supplierThatThrowsException.get());
    }
    catch (Exception e) {
        //logger.warn(e.getMessage()); //TODO add a real logger here
        System.err.println("logger warning: " + e.getMessage());
    }
    return Optional.empty();
};

This method gets a SupplierWithException<T> as parameter, that is then executed. Therefore you need to create the functional interface SupplierWithException<T> like this:

@FunctionalInterface
public interface SupplierWithException<T> {
    
    public T get() throws Exception;
}

Now the method can be used like this (assuming the method is defined in the class ExceptionWrapperUtil):

public static void main(String[] args) {
    Optional<Integer> result1 = ExceptionWrapperUtil.wrappException(//
            //create a SupplierWithException object (as lambda expression, because it's a functional interface)
            //when the get() method is called (within the wrappException method) the calculation is started
            () -> {
                return doCalculations(true);//will throw an exception that is logged
            });
    
    Optional<Integer> result2 = ExceptionWrapperUtil.wrappException(() -> {
        return doCalculations(false);//will succeed without an exception
    });
    
    System.out.println("result1 present: " + result1.isPresent());
    System.out.println("result2 present: " + result2.isPresent());
    System.out.println("result2: " + result2.get());
}

public static int doCalculations(boolean throwException) {
    boolean failed = throwException;//test throwing an exception
    
    //TODO do some calculations...
    int theAnswer = 42;
    
    if (failed) {
        throw new IllegalStateException("calculations failed");
    }
    
    return theAnswer;
}

The test generates the following output:

logger warning: calculations failed
result1 present: false
result2 present: true
result2: 42


complete example code

import java.util.Optional;

public class ExceptionWrapperUtil {
    
    public static <T> Optional<T> wrappException(SupplierWithException<T> supplierThatThrowsException) {
        try {
            return Optional.of(supplierThatThrowsException.get());
        }
        catch (Exception e) {
            //logger.warn(e.getMessage()); //TODO add a real logger here
            System.err.println("logger warning: " + e.getMessage());
        }
        return Optional.empty();
    };
    
    @FunctionalInterface
    public interface SupplierWithException<T> {
        
        public T get() throws Exception;
    }
    
    public static void main(String[] args) {
        Optional<Integer> result1 = ExceptionWrapperUtil.wrappException(//
                //create a SupplierWithException object (as lambda expression, because it's a functional interface)
                //when the get() method is called (within the wrappException method) the calculation is started
                () -> {
                    return doCalculations(true);//will throw an exception that is logged
                });
        
        Optional<Integer> result2 = ExceptionWrapperUtil.wrappException(() -> {
            return doCalculations(false);//will succeed without an exception
        });
        
        System.out.println("result1 present: " + result1.isPresent());
        System.out.println("result2 present: " + result2.isPresent());
        System.out.println("result2: " + result2.get());
    }
    
    public static int doCalculations(boolean throwException) {
        boolean failed = throwException;//test throwing an exception
        
        //TODO do some calculations...
        int theAnswer = 42;
        
        if (failed) {
            throw new IllegalStateException("calculations failed");
        }
        
        return theAnswer;
    }
}

You can do some improvements to the answer that @Tobias has provided.

In the question, I can see you want to pass some input of generic type (let assume T) and expect and Optional<R> type.

This is new functional interface look like,

@FunctionalInterface
public interface FunctionException<T, R> {
        public R apply(T t) throws Exception;
}

Then new expectation wrapper,

public <T,R> Optional<R> wrapException(Function<T, R> function, T t) {
        try { return Optional.of(function.apply(t)); } 
        catch (Exception e) {
            System.out.println(e.getLocalizedMessage());
            //  todo: add logger
        }
        return Optional.empty();
};

You can pass any method that have following signature to wrapException method.

R yourMethod(T t) throws Exception

Here is an example,

public boolean someMethod(int in) throws IOException {
        // your business logic

        // my business logic
        if (in == 4) throw new IOException("Some exception"); 
        return in == 3;
};

You can call wrapException like this

Optional<Boolean> a = wrapException(YourClass::someMethod, 2);
Optional<Boolean> b = wrapException(YourClass::someMethod, 4);
Related