2010-12-11 10 views
7

Also habe ich diese Frage schon einmal gestellt, aber ich hatte einen Fehler im Code, den die meisten Leute aufgriffen, anstatt das Problem selbst.Java: Ersetzen einer Unterklasse/Unterart eines Parameters beim Überschreiben einer Methode?

Wie auch immer, ich versuche, eine Interface-Methode in einer Klasse zu überschreiben. Ich möchte jedoch, dass der Typ des Parameters in der überschreibenden Methode eine Unterklasse des Typs des Parameters ist, wie er in der Methode overriden definiert ist.

Die Schnittstelle ist:

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

Während die Klasse, die diese Methode überschreibt ist:

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 eine Unterklasse von ComponentUpdateEvent ist.

Jetzt könnte ich nur die update() -Methode in ConsoleDrawer ein ComponentUpdateEvent als Parameter und dann auf ein ConsoleUpdateEvent werfen, aber ich suche nach einer etwas eleganteren Lösung, wenn möglich. Anyhelp würde geschätzt werden. Vielen Dank.

+5

Ich glaube nicht, dass dies möglich ist, da es dem Prinzip einer Schnittstelle widerspricht (d. H. Klassen, die eine Schnittstelle implementieren, stimmen genau mit allen von der Schnittstelle angegebenen Methodensignaturen überein). Ich werde diese Frage jedoch beobachten, da ich sehr daran interessiert bin zu sehen, ob jemand anderes mir das Gegenteil beweisen wird. – DGH

Antwort

2

Sie könnten Folgendes versuchen. Das @Deprecated erzeugt eine Warnung, wenn der Compiler weiß, dass Sie die erste Methode und nicht die zweite aufrufen werden.

@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: Sie sollten nicht nur Platz Ausnahme auf alles wirft. Es ist sicherlich nicht die beste Praxis.

EDIT: Ich habe eine andere Lösung für dieses Problem implementiert, die gut mit OSGi funktioniert, aber überall funktionieren kann.

Ein Observer registriert sich selbst bei einem Broker und erwartet Methoden mit einer Anmerkung wie ObserverCallback.

z.B.

Im ersten Fall findet der Broker eine Methode mit dem @ObserverCallback, die ein Argument benötigt. Dies ist der einzige Typ, den der Broker übergeben wird. Die zweite Klasse erwartet einen anderen Typ. Beobachter können mehrere Methoden/Typen haben, die es ihnen ermöglichen, verschiedene Nachrichten in verschiedenen Methoden zu verarbeiten, die für diesen Typ geeignet sind. Sie wissen auch, dass Sie niemals einen Datentyp erhalten, den Sie nicht erwarten.

+0

Ich persönlich (und viele andere, mit denen ich gesprochen habe) mag es nicht, das Attribut Veraltet für etwas anderes als die ursprüngliche Absicht zu verwenden: Markierungsmethoden, die früher gültig waren, aber jetzt veraltet sind. Für etwas anderes zu benutzen, fühlt sich hacker-isch an. – DGH

+0

@DGH, Die verwendete Methode war gültig (für das übergeordnete Element), ist aber für diese Klasse nicht mehr gültig. ;) Ich nehme aber deinen Standpunkt an. Idealerweise würde es ein Tag geben, das einen Compilerfehler erzeugt. Noch besser wäre es, die update() -Methode so zu gestalten, dass alle Implementierungen den Vertrag honorieren. –

+0

Danke für den Vorschlag. Verwenden der ersten Lösung; Ich habe die zweite Update-Methode in private void addEventMessages (ConsoleUpdateEvent) {// ...} umbenannt und das scheint einigermaßen gut zu funktionieren. – carnun

7

Sie können nicht. Das ist nicht Eiffel. Das Problem ist, dass Sie die Schnittstelle verwenden können, um die Implementierungsmethode mit einem inkompatiblen Typ aufzurufen. Daher sind kovariante Parameter nicht erlaubt. Kontravariante Parameter sind ebenfalls nicht erlaubt, es ist jedoch einfacher, eine Überladung bereitzustellen. Kovarianter Rückgabetyp ist erlaubt (seit 1.5).

Sie die Schnittstelle parametriert werden könnte:

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

Alternativ können Sie eine aussagekräftigere Schnittstelle:

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

Gefunden detaillierte Antwort hier http://StackOverflow.com/A/9421315/1276636 – Sufian

2

Going durch OOP-Prinzipien, eine Unterklasse sollte verwendbar sein in genau der gleichen Weise wie die Elternklasse. z.B.

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

Wenn jedoch Java war, damit Sie einen Subtyp des Parameters zu verwenden, wenn eine Methode überschreiben, dann wäre es, Ihren Code zu Fällen aussetzen, wo die übergeordnete Methode (in der Unterklasse) den Eingangsparameter ablehnen würde (ComponentUpdateEvent im obigen Fall). Somit wären Sie nie sicher, ob es sicher ist, update() oder nicht auf einer Observer-Referenz aufzurufen.

Daher ist die einzige logische Lösung, den Parent-Class-Parameter zu akzeptieren, type-check es und dann auf den erforderlichen Subtype zu werfen.

Verwandte Themen