Exceptions entre classes publiques et classes internes statiques publiques

J’ai deux options (qui sont techniquement les mêmes, si je comprends bien) pour déclarer une classe d’exception personnalisée lancée uniquement à partir d’une classe particulière com.XXX.Foo :

  • en tant que classe publique dans le package: com.XXX.CustomException
  • en tant que classe interne statique publique: com.XXX.Foo.CustomException

Quelle option est la meilleure?

Si l’exception est très spécifique à la classe Foo , cela ne me dérange pas de la garder en tant que classe public Nested. Et chaque fois que cela semble être le moment d’extraire cela, extrayez-le simplement.

En pratique générale, cependant, je n’ai jamais vu de classe nestede définie pour Exception. Je ne sais pas non plus s’il en existe une dans l’API Java.

Dans mon expérience de plus de 10 ans avec Java, je ne me souviens pas d’avoir rencontré une API dans laquelle une classe d’exception publique était définie comme une classe interne statique. Je ne peux pas vous donner une raison précise pour laquelle ce serait une mauvaise idée de le faire, mais cela rendrait certainement votre code inhabituel .

Pourquoi pensez-vous qu’il est nécessaire de faire cela, étant donné que (apparemment) personne d’autre ne le fait? J’espère que vous ne le faites pas simplement pour être “innovant”.

(BTW, je sais que certaines API Java bien connues utilisent des interfaces et des classes internes statiques publiques pour d’autres choses. Je parle spécifiquement du cas des classes d’exception ici.)

Je peux certainement penser à des situations où je préférerais que l’exception soit une classe interne statique plutôt qu’une simple classe dans le même paquet.

Les raisons pour ne pas le faire semblent être:

  • Coupler l’exception à la classe qui le lève, il est inapproprié de le réutiliser dans d’autres contextes.
  • Personne d’autre ne le fait

Je ne trouve aucun de ces arguments convaincant.

Pour le premier point, pourquoi cette possibilité future de réutilisation pourrait-elle apparaître dans le même paquet? Cet argument nous amène à la conclusion que nous devrions placer toutes les classes d’exceptions aussi haut que possible dans la hiérarchie des paquets, de sorte que lorsque nous découvrirons une opportunité future de réutiliser la même exception, nous ne devrons pas introduire de dépendance à l’origine.

Mais même sans le point “pris aux extrêmes”, considérons une exception destinée à transmettre la classe à laquelle Foo a reçu une mauvaise entrée. Si je l’appelle Foo.InvalidInput , le nom est court et l’association avec Foo est impossible à manquer. Si je le mets en dehors de la classe Foo et que je l’appelle FooInvalidCriteria , alors je ne peux pas le réutiliser de la classe Bar sans changer son nom (ce qui équivaut à changer son emplacement).

Mais le pire, c’est si je le laisse en dehors de Foo et conserve son nom général, comme InvalidInput . Puis, lorsque je me suis rendu compte par la suite que Bar pouvait avoir une entrée non valide également, je le fais commencer à lever cette exception. Tout se comstack et fonctionne InvalidInput , mais maintenant, tous les endroits qui InvalidInput et supposaient qu’ils InvalidInput les erreurs de Foo pouvaient maintenant gérer les erreurs de Bar si Foo utilisait Bar interne de manière à provoquer cette exception. Cela pourrait facilement provoquer la rupture du code.

La réalité est que prendre une exception qui a été conçue précédemment comme indiquant spécifiquement une situation qui survient dans une classe et la réutiliser en tant que classe d’erreur générale est un changement d’ interface , pas seulement un changement d’implémentation interne. Pour le faire correctement en général, vous devez revoir tous les sites où l’exception est interceptée et vous assurer qu’ils sont toujours corrects. Le compilateur doit donc vous informer de tous les sites d’utilisation (car vous devez modifier le nom et / ou le chemin d’importation). ) c’est une bonne chose. Toute exception que vous pourriez créer avec une classe interne statique est inappropriée pour une réutilisation dans d’autres contextes, que vous en fassiez ou non une classe interne.

Et pour ce qui est du second point, «personne ne le fait» ne porte jamais sur quoi que ce soit. Soit c’est vraiment la mauvaise chose à faire, donc il y aura d’autres raisons de ne pas le faire, donc l’argument “personne d’autre ne le fait” est inutile. Ou ce n’est pas. Et ce n’est pas comme si cet exemple particulier était même terriblement compliqué et difficile à comprendre, donc l’argument de “ce n’est pas surprenant, donc les gens auront du mal à le suivre même si c’est une bonne idée en théorie”.

Je préférerais la classe (pas nécessairement publique) dans le même package, car un package est un groupe logique de classes représentant un modèle d’entreprise, auquel l’exception appartient en tant que partie technique.

Un utilisateur verra immédiatement qu’il y a une exception lorsqu’il regarde le paquet et qu’il n’a pas besoin de lire le fichier de la classe foo, ce qui est préférable pour des raisons de maintenance et de clarté / lisibilité / compréhension. C’est très bien de définir des exceptions personnalisées et d’en informer l’utilisateur de l’API!

Je n’utiliserais qu’une classe interne quand c’est clairement une chose privée de la classe en question.

Néanmoins, nous parlons ici d’un problème principalement conventionnel!

Les exceptions en tant que classes internes sont une mauvaise idée en général car, par nature, elles peuvent être levées à différents niveaux, même dans des architectures simples. La classe d’exception levée doit être référencée à partir de la classe qui la contient, et si cette classe est inconnue du chemin de classe, quelque chose de mauvais se produira probablement, comme une ClassCastException.