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

87 Views Asked by At

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?

1

There are 1 best solutions below

3
Roma S On BEST ANSWER
  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