2010-12-11 6 views
7

J'ai donc posé cette question auparavant, mais j'ai eu une erreur dans le code que la plupart des gens ont choisi, plutôt que le problème lui-même.Java: substitution d'une sous-classe/sous-type d'un paramètre lors de la substitution d'une méthode?

Quoi qu'il en soit, j'essaie de remplacer une méthode d'interface dans une classe. Cependant, je souhaite que le type du paramètre dans la méthode de substitution soit une sous-classe du type du paramètre tel que défini dans la méthode overrid.

L'interface est:

public interface Observer { 
public void update(ComponentUpdateEvent updateEvent) throws Exception; 
} 

Alors que la classe qui remplace cette méthode est:

public class ConsoleDrawer extends Drawer { 

//... 

@Override 
public void update(ConsoleUpdateEvent updateEvent) throws Exception { 
    if (this.componentType != updateEvent.getComponentType()) { 
    throw new Exception("ComponentType Mismatch."); 
    } 
    else { 
    messages = updateEvent.getComponentState(); 
    } 
} 

//... 

} 

ConsoleUpdateEvent est une sous-classe de ComponentUpdateEvent.

Maintenant, je pourrais juste avoir la méthode update() dans ConsoleDrawer prendre un ComponentUpdateEvent en tant que paramètre, puis le transformer en ConsoleUpdateEvent mais je cherche une solution un peu plus élégante si possible. Toute aide serait appréciée. Je vous remercie.

+5

Je ne crois pas que cela soit possible car cela va à l'encontre du principe d'une interface (c'est-à-dire que les classes qui implémentent une interface correspondent exactement à toutes les signatures de méthodes spécifiées par l'interface). Cependant, je vais regarder cette question car je suis très intéressé de voir si quelqu'un d'autre va me prouver le contraire. – DGH

Répondre

2

Vous pourriez essayer ce qui suit. Le @Deprecated produit un avertissement si le compilateur sait que vous allez appeler la première méthode plutôt que la seconde.

@Override @Deprecated 
public void update(ComponentUpdateEvent updateEvent) { 
    // throws a ClassCastException if its not the right type. 
    update((ConsoleUpdateEvent) updateEvent); 
} 

public void update(ConsoleUpdateEvent updateEvent) { 
    messages = updateEvent.getComponentState(); 
} 

BTW: Vous ne devez pas simplement placer throws Exception sur tout. Ce n'est certainement pas la meilleure pratique.

EDIT: J'ai implémenté une solution différente à ce problème qui fonctionne bien avec OSGi mais peut fonctionner n'importe où. Un observateur s'enregistre auprès d'un courtier et s'attend à trouver des méthodes avec une annotation telle que ObserverCallback.

par exemple.

public class ConsoleDrawer extends Drawer { 
@ObserverCallback 
public void onConsoleUpdateEvent(ConsoleUpdateEvent updateEvent) { 
    messages = updateEvent.getComponentState(); 
} 
} 

public class DeviceDrawer extends Drawer { 
@ObserverCallback 
public void onDeviceUpdateEvent(DeviceUpdateEvent updateEvent) { 
    // do something. 
} 
} 

Dans le premier cas, le courtier trouve un procédé avec l'@ObserverCallback qui prend un argument. C'est le seul type que le courtier passera. La deuxième classe attend un type différent. Les observateurs peuvent avoir plusieurs méthodes/types leur permettant de gérer différents messages selon différentes méthodes appropriées à ce type. Vous savez également que vous ne recevrez jamais un type de données auquel vous ne vous attendez pas.

+0

Personnellement (et beaucoup d'autres personnes à qui j'ai parlé) je n'aime pas utiliser la balise Deprecated pour autre chose que l'intention originale: marquer des méthodes qui étaient valides mais qui sont maintenant obsolètes. L'utiliser pour quelque chose d'autre ressemble à un hacker-ish. – DGH

+0

@DGH, La méthode utilisée pour être valide (pour le parent) mais n'est plus valide pour cette classe. ;) Je prends votre point cependant. Idéalement, il y aurait une balise qui produirait une erreur de compilation. Encore mieux serait de concevoir la méthode update() de sorte que toute l'implémentation honore le contrat. –

+0

Merci pour la suggestion. En utilisant la première solution J'ai renommé la seconde méthode de mise à jour en addEventMessages (ConsoleUpdateEvent) {} ...} et cela semble fonctionner correctement. – carnun

7

Vous ne pouvez pas. Ce n'est pas Eiffel. Le problème étant que vous pourriez utiliser l'interface pour appeler la méthode d'implémentation avec un type incompatible. Les paramètres covariants ne sont donc pas autorisés. Les paramètres contravariants ne sont pas autorisés non plus, mais il est plus facile de fournir une surcharge. Le type de retour covariant est autorisé (depuis 1.5).

Vous pouvez paramétrer l'interface:

public interface Observer<T extends ComponentEvent> { 
    void update(T event) throws Exception; 
} 

Vous pouvez également utiliser une interface plus significative:

public interface ConsoleObserver { 
    void update(ConsoleEvent event) throws Exception; 
} 
+1

trouvé réponse détaillée ici http://stackoverflow.com/a/9421315/1276636 – Sufian

2

Going par les principes de la POO, une sous-classe devrait être utilisable exactement de la même manière que la classe parente. par exemple.

Observer ob = new ConsoleDrawer(); 
ob.update(new ComponentUpdateEvent()); // This needs to work always. 

Cependant, si Java devait vous permettre d'utiliser un sous-type du paramètre lors de la substitution d'une méthode, il exposerait votre code à des cas où la méthode dérogatoire (dans la sous-classe) rejette le paramètre d'entrée (ComponentUpdateEvent dans le cas ci-dessus). Ainsi, vous ne saurez jamais s'il est sûr d'appeler update() ou non sur une référence Observer. Par conséquent, la seule solution logique consiste à accepter le paramètre de la classe parente, le vérifier par type, puis le convertir en sous-type requis.