The reason for using the interface is to abstract the implementation details.
By throwing these exceptions, you are revealing implementation details that should probably be abstracted.
Perhaps it would be better to define a new exception. Then each implementation of f () will catch those exceptions that it knows about, and instead throw a new exception so that you have:
interface I { void f() throws MyException; } class A implements I { public void f() throws MyException { try { ... } catch (IOException e) { throw new MyException(e); } } } class B implements I { public void f() throws MyException { try { ... } catch (InterruptedException e) { throw new MyException(e); } } }
Concluding the implementation exception, you expose it to the caller anyway, and this may bite you when you call remote methods. In these cases, you need to do more work to return useful information in a general way.
Edit
There seems to be a bit of a debate about the right approach.
When we call f (), we need the code:
I instanceOfI = getI(); try { instanceOfI.f(); } catch ( )
This boils down to what is a good Exception class to include a catch block.
With the OP source code, we could catch an Exception , and then maybe try to see which subclass we have or is not dependent on requirements. Or we could individually catch each subclass, but then we would have to add catch blocks when new implementations throw different exceptions.
If we used Runtime exceptions, it would be almost the same, except that we could alternatively defer exception handling to the caller method, without even giving the possibility of exceptions of any thought.
If we used my suggestion to use a new wrapped exception, this means that we should catch MyException , and then try to find out what additional information is available. This is essentially very similar to using an exception, but requires additional work for the limited benefit of a bespoke exception that can be tailored to the target.