Pour illustrer cela, considérons:
public interface FileOperation {
void perform(File file) throws FileNotFoundException;
}
public class OpenOnly implements FileOperation {
void perform(File file) throws FileNotFoundException {
FileReader r = new FileReader(file);
}
}
Supposons que vous ecrivez:
public class OpenClose implements FileOperation {
void perform(File file) throws FileNotFoundException {
FileReader r = new FileReader(file);
r.close();
}
}
Cela vous donnera une erreur de compilation, car r.close() renvoie une IOException, qui est plus large que FileNotFoundException.
Pour résoudre ce problème, si vous écrivez:
public class OpenClose implements FileOperation {
void perform(File file) throws IOException {
FileReader r = new FileReader(file);
r.close();
}
}
Vous allez obtenir une autre erreur de compilation, parce que vous êtes la mise en œuvre de l'exécution (...), mais le fait de lancer une exception non inclus dans l'interface de définition de la méthode.
Pourquoi est-ce important? Ainsi un consommateur de l'interface:
FileOperation op = ...;
try {
op.perform(file);
}
catch (FileNotFoundException x) {
log(...);
}
Si le IOException ont été autorisés à être jetés, le code du client est de ne pas corriger.
Notez que vous pouvez éviter ce genre de problème si vous utilisez décoché exceptions. (Je ne dis pas que vous faites ou ne faites pas, c'est un problème philosophique)