javalambdainterfacefunctional-interface

How to write a Functional Interface that throws the same checked exception


I know Runnable does not throw Exception, so I am trying to write a new interface that accepts method that can throw checked exception:

Interface:

interface MyInterface {
    void run() throws Exception;
}

Service:

public void serviceMethod(MyInterface action) throws Exception {
    // ...
    action.run();
    // ...
}
public void serviceMethod(Runnable action) { // for methods don't throw checked exception
    // ...
    action.run();
    // ...
}

Main:

public void myMethod() throws Exception {
    service.serviceMethod(this::someMethodThatThrowsCheckedException);
    service.serviceMethod(this::someMethodThatDoesNotThrowCheckedException);
}

This all seems OK, but I don't want myMethod() to throw the base class Exception, I want it to throw the actual exception classes throw by someMethodThatThrowsCheckedException. e.g. if

public void someMethodThatThrowsCheckedException throws IOException, ClassNotFoundException {//...}

Then I want myMethod() to throw IOException, ClassNotFoundException, instead of Exception. Just like the case when the method is called normally without going through the interface:

public void myMethod() throws IOException, ClassNotFoundException {
    someMethodThatThrowsCheckedException();
}

Can this be done in a generic way?


Solution

    1. You can create interface that throws generic exception:
        interface MyInterface<T extends Exception> {
            void run() throws T;
        }
    

    And one that not throws

        interface MySuperInterface extends MyInterface<Exception> {
            void run();
        }
    

    usage:

    MyInterface<IOException> io = () -> {throw new IOException();};
    MyInterface<IllegalAccessException> iae = () -> {throw new IllegalAccessException();};
    
    1. You also can create generic consumer method:

    One for throws:

        public static <T extends Exception> void consume(MyInterface<T> action) throws T {
            action.run();
        }
    

    One for no throws:

        public static void consume(MySuperInterface action) {
            action.run();
        }
    
    1. And you be able to do this:
        public static void mainMethod() throws IOException, IllegalAccessException {
            MyInterface<IOException> io = () -> {throw new IOException();};
            MyInterface<IllegalAccessException> iae = () -> {throw new IllegalAccessException();};
            MySuperInterface noException = () -> {};
            //can assign to runnable cause noException.run() doesnt throw an exception
            Runnable runnable = noException::run;
    
            consume(io);
            consume(iae);
            consume(noException);
        }
    
    1. Full example:
        interface MyInterface<T extends Exception> {
            void run() throws T;
        }
    
        interface MySuperInterface extends MyInterface<Exception> {
            void run();
        }
    
        public static void main(String[] args) throws Exception {
            mainMethod();
        }
    
        public static void mainMethod() throws IOException, IllegalAccessException {
            MyInterface<IOException> io = () -> {throw new IOException();};
            MyInterface<IllegalAccessException> iae = () -> {throw new IllegalAccessException();};
            MySuperInterface noException = () -> {};
            //can assign to runnable cause noException.run() doesnt throw an exception
            Runnable runnable = noException::run;
    
            consume(io);
            consume(iae);
            consume(noException);
        }
    
        public static <T extends Exception> void consume(MyInterface<T> action) throws T {
            action.run();
        }
    
        public static void consume(MySuperInterface action) {
            action.run();
        }
    

    Hope this will help you