7 votes

Omettre les déclarations throws dans les classes dérivées

Considérons l'interface suivante :

public interface Generator {
    String generate() throws IOException;
}

et la mise en œuvre suivante :

public class EmptyStringGenerator implements Generator {
    @Override
    public String generate() {
        return "";
    }
}

Notez que j'ai omis le throws IOException la partie de la signature spécifiée dans le Generator interface. Pourtant, il n'y a pas d'erreur de compilation, pas d'avertissement du compilateur, pas même l'icône @Override L'annotation se plaint.

Je suis conscient que cela fonctionne comme prévu. J'aimerais toutefois connaître l'intention qui se cache derrière cette méthode. Si ma méthode ne lance pas réellement un IOException Je ne suis pas obligé de la retirer de la signature. Mais si je le supprime de la signature de ma méthode dans la section EmptyStringGenerator je force toutes les sous-classes actuelles et futures de cette classe à renoncer à la possibilité de lancer une exception qui est effectivement spécifiée dans l'interface.

Pour moi, il s'agit d'une fonctionnalité qui ne présente pas vraiment d'avantages (à part l'économie de quelques frappes, ce qui n'est pas vraiment un avantage), mais qui peut être une terrible erreur lorsqu'elle est utilisée.

Donc ma question, en fait, est la suivante : Quel est l'intérêt d'omettre throws les exceptions dans les classes dérivées ? Quel est le problème que cette possibilité résout ? Pourquoi cette possibilité est-elle autorisée ?

UPDATE

Pour les personnes qui se demandent "mais où est le mal ?", voici l'exemple tiré d'un de mes commentaires. Il n'est pas tiré par les cheveux, d'ailleurs, car c'est exactement ce à quoi je suis confronté en ce moment :

Le programmeur A spécifie l'interface I. Le programmeur B écrit la classe d'implémentation X, mais oublie d'ajouter les lancers. Il ne s'en aperçoit pas non plus, car il n'y a même pas d'avertissement lancé ici. Le programmeur C écrit la classe d'implémentation Y, héritant de la classe X, il veut même spécifiquement mettre les throws là, parce qu'il va lancer. Mais même si l'interface le stipule, il n'est maintenant plus autorisé à le faire, à cause de l'oubli de B. Il n'est effectivement plus autorisé à utiliser cette exception ici. C'est un préjudice assez important. Surtout si la classe X n'est pas sous votre contrôle.

1voto

Jimmy T. Points 1368

Si vous omettez le throws dans les classes dérivées, vous pouvez appeler la méthode de la classe dérivée sans avoir à attraper l'exception.

Vous devez vous assurer que les sous-classes de EmptyStringGenerator ne lèvent pas non plus d'exception. Sinon, le compilateur ne serait pas sûr que l'appel de méthode puisse provoquer une exception vérifiée que le code doit gérer. Dans ce cas, le throws n'aurait pas de sens du tout.

0voto

judaschrist Points 1

Sur la base de la déclinaison de votre classe, comparez ces deux conditions :

    EmptyStringGenerator generator = new EmptyStringGenerator();
    generator.generate();

si vous créez une instance de EmptyStringGenerator comme ci-dessus, Puisque vous omettez le throws dans EmptyStringGenerator, le code ci-dessus indique que vous utilisez la classe elle-même, ce qui bien sûr n'a rien à voir avec l'interface, donc le code fonctionne très bien.

mais si vous avez l'intention d'utiliser l'interface au lieu de la classe, comme ceci :

    Generator generator = new EmptyStringGenerator();
    generator.generate();

que le compilateur ne vous rappelle qu'il y a une exception non gérée, vous devez la gérer avec un try-catch ou la lancer, sinon le code ne compilera pas.

si vous utilisez une sous-classe de EmptyStringGenerator de cette dernière manière, la même erreur de compilation se produira. Ainsi, le fait d'omettre le throw ne vous dispense pas de traiter l'exception. Il est tout à fait naturel de ne pas lancer d'exception dans une classe et ses sous-classes lorsqu'il n'y en a aucune à lancer, mais lorsque vous appelez la méthode par l'intermédiaire de l'interface, l'exception doit quand même être traitée.

0voto

mszalbach Points 1381

En Java, il est normal et acceptable que vous puissiez rendre vos classes moins restrictives lors de l'implémentation ou de l'extension, c'est une décision de conception du développeur Java. Je ne peux pas dire pourquoi Sun a pris cette décision et, bien sûr, je peux comprendre votre problème, mais la méthode actuelle présente aussi des avantages.

Je vois une interface comme une possibilité d'avoir plusieurs implémentations pour un même travail. Par exemple, les classes de liste qui ont différentes implémentations pour différents besoins. Mais elles peuvent toutes être utilisées via l'interface List. Supposons que la méthode de suppression lève une CheckedException si l'élément ne fait pas partie de la liste. Si je ne suis pas en mesure d'être moins restrictif dans mes implémentations, toutes les classes List doivent lancer cette CheckedException, même si elles n'en ont pas besoin ou ne l'utilisent pas.

Ainsi, si j'utilise la méthode de suppression en interne dans ma classe, je suis obligé de gérer la CheckedException. Si j'utilise ma classe List directement sans l'interface, je suis obligé d'attraper l'exception. Mais dans les deux cas, je suis pratiquement sûr que je n'en ai pas besoin et que cela n'arrivera jamais. Avec l'approche actuelle, je peux donc économiser beaucoup de blocs "try catch ignore".

Un autre avantage de la solution actuelle est qu'une classe peut facilement correspondre à des interfaces similaires.

Ainsi, par exemple, dans une librairie, quelqu'un a ajouté un :

public interface Generator {
    String generate() throws IOException;
}

Et dans un autre livre :

public interface GeneratorInterface {
    String generate();
}

Si j'écris une classe avec une méthode de génération sans aucune CheckedException, je peux facilement l'utiliser pour satisfaire les deux interfaces et peut-être travailler avec les deux librairies :

public class EmptyStringGenerator implements Generator,GeneratorInterface {

    @Override
    public String generate() {
        return "";
    }
}

De plus, pour autant que je sache, Java est le seul langage qui gère les exceptions vérifiées et non vérifiées. Les décisions de conception qui sont prises par Java sont donc étranges par rapport aux autres langages qui n'ont pas d'exceptions vérifiées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X