2009-08-03 13 views
16

Quelle est la meilleure pratique à suivre lorsque vous devez lever une exception qui n'a pas été définie dans une interface que vous implémentez?Lance une exception non définie dans l'interface

Voici un exemple:

public interface Reader 
{ 
    public abstract void read() throws IOException; 
} 

public class CarrotReader implements Reader 
{ 
    public void read() throws IOException {} 
} 

public class CupcakeReader implements Reader 
{ 
    public void read() throws IOException, CupcakeException {} 
} 

Dans ce cas, vous avez une exception qui se produit lors de la lecture des petits gâteaux, de sorte que vous voulez lancer une exception liée à cela. Cependant, Reader ne définit pas ce type d'exception dans son interface, alors que faites-vous? En outre, il n'a pas de sens pour ajouter CupcakeException à l'lancers francs l'interface clause du lecteur, parce que ce type d'exception est spécifique à CupcakeReader. Une façon de contourner cela est d'avoir Reader définir lire de sorte qu'il lève un certain type parent, comme Exception, mais vous perdez le contexte de l'exception. Que devriez-vous faire dans cette situation? Merci!


Une autre situation intéressante qui a été soulevée concerne une interface sur laquelle vous n'avez aucun contrôle. Dans ce cas, quelle est la meilleure façon d'indiquer qu'un problème est survenu?

À titre d'illustration, voici un autre exemple:

public interface Reader 
{ 
    public abstract void read(); 
} 

public class CupcakeReader implements Reader 
{ 
    public void read() throws CupcakeException {} 
} 

Dans ce cas, vous ne pouvez pas changer lecteur, mais vous voulez indiquer qu'un problème est survenu dans de CupcakeReader lire méthode.

+0

C'est encore pire que ce que je pensais. De cette façon, vous ne pouvez même pas utiliser le chaînage pour contourner le système. Je pense que vous devez recourir à (ouch!) 'RuntimeExceptions'. –

+0

J'ai eu recours à System.err.println et à exception.printStackTrace() ;. Est-ce approprié, ou devrais-je propager l'erreur avec un RuntimeExecption comme vous l'avez dit? – Scott

+3

Je pense que log + swallow n'est pas approprié 99,99% du temps. C'est un modèle commun (anti -?) Parmi les développeurs Java à cause du problème présenté ici. Dans mon «école de pensée», il n'y a que trois façons valides de traiter les exceptions: la réitération, la manipulation (c'est-à-dire réparer le problème), ou laisser le soin à quelqu'un de s'en occuper. N'avale jamais. Si quelque chose ne va pas, il y aura des conséquences. –

Répondre

12

Vous devrez peut-être créer une exception du type attendu à la place.

... catch(CupcakeException e) { 
    throw new IOException("The sky is falling", e); 
} 
+1

+1 pour avoir mentionné le chaînage d'exceptions. –

+0

C'est ce que je suis en train de faire. – Scott

+0

C'est probablement la meilleure solution si vous ne pouvez pas changer l'interface. Vous pouvez toujours accéder à toutes les informations dont vous pourriez avoir besoin, et vous suivez toujours le contrat de l'interface. –

0

Peut-être pourriez-vous créer une classe ReaderSpecificException abstraite, la placer dans l'interface, et sous-classe CupcakeException de cette classe abstraite.

+1

Je ne pense pas que ce soit une bonne pratique de créer une exception en tant que classe abstraite. Par défaut, une exception sans attributs peut toujours fournir un aperçu du problème (simplement en choisissant le bon nom). Pourquoi voudriez-vous le définir comme une classe abstraite? – Manglu

9

Utilisez quelque chose appelé ReaderException qui servira d'interface racine de votre hiérarchie d'exception. ReaderException fournit également un lien vers d'autres exceptions renvoyées en raison d'exceptions de niveau inférieur.

+0

N'est-ce pas la même chose que d'ajouter une exception à la clause throws? Je me rends compte qu'en définissant votre propre classe de parents qui est un peu plus spécifique, vous préservez davantage le contexte, mais à un certain niveau, vous perdez toujours de l'information, n'est-ce pas? – Scott

+0

C'est ce que je fais habituellement (+1). Mais que feriez-vous si vous ne pouviez pas changer l'interface (le code n'est pas le vôtre, le code a déjà été livré, etc.)? –

+0

@Scott: vous ne perdez rien. Si vous lancez une 'CupcakeException' qui étend' ReaderException', vous pouvez l'attraper en tant que 'ReaderException' ou en tant que' CupcakeException', selon la manière dont vous voulez être. –

0

Si vous créez une exception abstraite supérieure qui fonctionne comme une classe de base pour CupCakeException vous ne lient pas l'interface lecteur à une implémentation spécifique, comme vous feriez si vous avez ajouté le CupCakeException à l'interface du lecteur. Si vous ne laissez pas une exception hériter d'une autre, il y a un constructor dans la classe d'exception qui prend un second argument comme Thorbjørn Ravn Andersen déjà montré dans son exemple de code court. Cela vous permet de générer une exception plus abstraite et chaque partie de votre code qui doit en savoir plus que "il y a une erreur" peut rechercher la cause de l'exception plus élevée.

1

N'utilisez simplement pas les exceptions vérifiées.

L'exemple que vous avez montré est l'une des raisons pour lesquelles les exceptions vérifiées sont mauvaises.

La raison principale est que l'utilisateur de votre lecteur cupcake devra gérer votre exception, qu'il s'y intéresse ou non.

Ainsi, au lieu de:

Value value = reader.read(); 

Vous le forçaient à faire:

Value value = null; 
try { 
    value = reader.read(); 
} catch (Exception e) { 
    // now what?? 
} 

value.doSomething(); // potential NPE here 

Pensez que l'on est mieux, plus lisible et moins sujette aux erreurs et juste cesser d'utiliser les exceptions vérifiées.

EDIT:

Je suis surpris par la note négative. Y a-t-il des gens qui pensent encore que les exceptions vérifiées sont bonnes? Si oui, voici quelques références pourquoi vous ne devriez pas utiliser les exceptions cochés:

  • Aucun cadre moderne utilise exceptions vérifiées (printemps, EJB3, etc.)
  • article avec des exemples de code here
  • StackOverflow topic
  • efficace Java (sections 58 et 59) - here
+4

Je pense que vous publiez un exemple délibérément trompeur. J'écrirais ce qui précède pour déclarer et utiliser la variable dans le bloc try {}. Il n'y a aucune raison de devoir écrire du code comme ci-dessus (que les exceptions soient vérifiées ou non) –

+5

Le problème avec les exceptions vérifiées n'est pas celui présenté ici. Comme Brian l'a souligné, ce code est juste faux. Un problème avec les exceptions vérifiées est que vous perdez tout avantage de faire des vérifications préemptives (quand vous le pouvez) avant d'appeler du code qui émet des exceptions. Même si vous effectuez des vérifications préemptives, vous êtes obligé d'utiliser un bloc try-catch. Un autre est le fait que si vous avez quelque chose que N appelle des deeps qui lancent, et que seul l'appel 0 peut le gérer, chaque appel de 0 à N doit inclure throws dans le contrat (ou triche avec 'RuntimeException's –

+0

Lisez ceci pour plus info sur le "pourquoi-pas" s des exceptions vérifiées: http://www.artima.com/intv/handcuffs.html –

2

L'exception fait partie de l'interface. Définissez un parent générique pour toutes vos exceptions dans l'interface si vous pouvez redéfinir l'interface.

Vous pouvez également faire de CupcakeException un enfant de IOException.

+0

Avoir un "vote up" pour le "aussi". Mais je suis partial, parce que c'est ce que j'aurais probablement fait (sous-classe de l'exception déclarée, si nécessaire). Bien sûr, avoir une sous-classe de (par exemple) IOException suppose que l'appelant a besoin de se soucier que quelque chose de plus spécifique est arrivé, tout en impliquant que le problème est vraiment un problème lié aux E/S. – Roboprog

Questions connexes