2010-12-11 11 views
7

Così ho fatto questa domanda prima ma ho avuto un errore nel codice che la maggior parte delle persone ha rilevato, piuttosto che il problema stesso.Java: sostituendo una sottoclasse/sottotipo di un parametro quando si sostituisce un metodo?

In ogni caso, sto provando a sovrascrivere un metodo di interfaccia in una classe. Tuttavia, voglio che il tipo di parametro nel metodo di sovrascrittura sia una sottoclasse del tipo del parametro come definito nel metodo sovrascritto.

L'interfaccia è:

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

Mentre la classe che ridefinisce questo metodo è:

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 è una sottoclasse di ComponentUpdateEvent.

Ora, ho potuto semplicemente avere il metodo update() in ConsoleDrawer prendere un ComponentUpdateEvent come parametro e quindi lanciarlo su un ConsoleUpdateEvent ma sto cercando una soluzione leggermente più elegante se possibile. Qualsiasi aiuto sarebbe apprezzato. Grazie.

+5

Non credo che questo sia possibile in quanto contrario al principio di un'interfaccia (ad esempio, le classi che implementano un'interfaccia corrisponderanno esattamente a tutte le firme di metodo specificate dall'interfaccia). Tuttavia, guarderò questa domanda poiché sono molto interessato a vedere se qualcun altro mi dimostrerà che ho torto. – DGH

risposta

2

Si potrebbe provare quanto segue. @Deprecated genera un avviso se il compilatore sa che chiamerai il primo metodo piuttosto che il secondo.

@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: non si deve solo inserire throws Exception su tutto. Non è certamente la migliore pratica.

EDIT: Ho implementato una soluzione diversa a questo problema che funziona bene con OSGi ma può funzionare ovunque.

Un osservatore si registra con un broker e si aspetta di trovare metodi con un'annotazione come ObserverCallback.

ad es.

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. 
} 
} 

Nel primo caso, il broker trova un metodo con il quale @ObserverCallback accetta un argomento. Questo è l'unico tipo che il Broker passerà. La seconda classe si aspetta un tipo diverso. Gli osservatori possono avere più metodi/tipi che consentono loro di gestire messaggi diversi in diversi metodi appropriati per quel tipo. Sai anche che non riceverai mai un tipo di dati che non ti aspetti.

+0

Personalmente (e molti altri con cui ho parlato) non mi piace usare il tag Deprecated per qualcosa di diverso dall'intenzione originale: contrassegnare metodi che erano validi ma ora sono deprecati. Usarlo per qualcos'altro come questo si sente hacker-ish. – DGH

+0

@DGH, il metodo utilizzato per essere valido (per il genitore) ma non è più valido per questa classe. ;) Prendo il tuo punto però. Idealmente ci sarebbe un tag che ha prodotto un errore del compilatore. Sarebbe anche meglio progettare il metodo update() in modo che tutte le implementazioni rispettassero il contratto. –

+0

Grazie per il suggerimento. Utilizzando la prima soluzione; Ho rinominato il secondo metodo di aggiornamento a private addEventMessages (ConsoleUpdateEvent) {// ...} e sembra funzionare abbastanza bene. – carnun

7

Non è possibile. Questa non è Eiffel. Il problema è che potresti usare l'interfaccia per chiamare il metodo di implementazione con un tipo incompatibile. Quindi i parametri covarianti non sono ammessi. Non sono ammessi neanche i parametri controvarianti, ma è più facile fornire un sovraccarico. Il tipo di ritorno covariante è consentito (dal 1.5).

Si potrebbe parametrizzare l'interfaccia:

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

In alternativa, utilizzare un'interfaccia più significativo:

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

Trovato risposta dettagliata qui http://stackoverflow.com/a/9421315/1276636 – Sufian

2

Andando da principi OOP, una sottoclasse dovrebbe essere utilizzabile esattamente nello stesso modo in cui la classe genitore. ad es.

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

Tuttavia, se Java dovesse consentire di utilizzare un sottotipo del parametro quando l'override un metodo, allora sarebbe esporre il codice per i casi in cui il metodo predominante (nella sottoclasse) avrebbe respinto il parametro d'ingresso (ComponentUpdateEvent nel caso precedente). Pertanto, non si sarà mai sicuri se è sicuro chiamare update() o meno su un riferimento Observer.

Quindi, l'unica soluzione logica è accettare il parametro parent della classe, digitarlo e quindi copiarlo nel sottotipo richiesto.

Problemi correlati